本文主要介绍AOP中5中通知的基本使用,以及相关术语的解释,欢迎大佬们批评指正!
Spring基础介绍
http://blog.youkuaiyun.com/a1n9n7e/article/details/77802793
什么是Spring、Strust、Hibernate
struts 是 web 框架(jsp/action/actionfrom)
hibernate 是 orm框架,处于持久层.
spring 是容器框架,用于配置bean,并维护bean之间关系的框架
Spring中重要概念:IOC / DI
IOC是什么?
ioc(inverse of controll ) 控制反转: 所谓控制反转就是把创建对象(bean),和维护对象(bean)的关系的权利从程序中转移到spring的容器(applicationContext.xml),而程序本身不再维护.
DI是什么?
di(dependency injection) 依赖注入: 实际上di和ioc是同一个概念,spring设计者认为di更准确表示spring核心技术。
依赖注入(DI)背后的基本原理是对象之间的依赖关系(即一起工作的其它对象)只会通过以下几种方式来实现:构造器的参数、工厂方法的参数,或给由构造函数或者工厂方法创建的对象设置属性。因此,容器的工作就是创建bean时注入那些依赖关系。相对于由bean自己来控制其实例化、直接在构造器中指定依赖关系或者类似服务定位器(Service Locator)模式这3种自主控制依赖关系注入的方法来说,控制从根本上发生了倒转,这也正是控制反转(Inversion of Control, IoC) 名字的由来。
应用DI原则后,代码将更加清晰。而且当bean自己不再担心对象之间的依赖关系(甚至不知道依赖的定义指定地方和依赖的实际类)之后,实现更高层次的松耦合将易如反掌。DI主要有两种注入方式,即Setter注入和构造器注入
AOP编程
容器和bean
什么是bean
在Spring中,那些组成你应用程序的主体(backbone)及由Spring IoC*容器所管理的对象,被称之为*bean。
简单地讲,bean就是由Spring*容器*初始化、装配及管理的对象,除此之外,bean就与应用程序中的其他对象没有什么区别了。
而bean定义以及bean相互间的依赖关系将通过配置元数据来描述。
什么是容器
org.springframework.beans.factory.BeanFactory
是Spring IoC*容器*的实际代表者,IoC容器负责容纳此前所描述的bean,并对bean进行管理。
在Spring中,BeanFactory
是IoC容器的核心接口。 它的职责包括:实例化、定位、配置应用程序中的对象及建立这些对象间的依赖。
Spring为我们提供了许多易用的BeanFactory
实现, XmlBeanFactory
就是最常用的一个。该实现将以XML方式描述组成应用的对象 以及对象间的依赖关系。XmlBeanFactory
类将获取此XML*配 置元数据*,并用它来构建一个完全可配置的系统或应用。
Bean的作用域
作用域 | 描述 |
---|---|
singleton | 在每个Spring IoC容器中一个bean定义对应一个对象实例。 |
prototype | 一个bean定义对应多个对象实例。 |
request | 在一次HTTP请求中,一个bean定义对应一个实例;即每次HTTP请求将会有各自的bean实例, 它们依据某个bean定义创建而成。该作用域仅在基于web的Spring ApplicationContext 情形下有效。 |
session | 在一个HTTP Session 中,一个bean定义对应一个实例。该作用域仅在基于web的Spring ApplicationContext 情形下有效。 |
global session | 在一个全局的HTTP Session 中,一个bean定义对应一个实例。典型情况下,仅在使用portlet context的时候有效。该作用域仅在基于web的Spring ApplicationContext 情形下有效。 |
注意:尽量使用scop=”singleton“,避免使用propotype,以为propotype对性能影响比较大。
Bean的生命周期
Bean的实例化与销毁
spring实例化bean或销毁bean时,有时需要作一些处理工作,因此spring可以在创建和拆卸bean的时候调用bean的两个生命周期方法。
<!-- 通过 配置init-method和destory-method,实现对bean的初始化,和销毁bean时的相关操作-->
<bean class="Foo" init-method="init" destory-method="destroy">
初始化回调
实现org.springframework.beans.factory.InitializingBean
接口允许容器在设置好bean的所有必要属性后,执行初始化事宜。InitializingBean
接口仅指定了一个方法:
void afterPropertiesSet() throws Exception;
通常,要避免使用InitializingBean
接口并且不鼓励使用该接口,因为这样会将代码和Spring耦合起来,有一个可选的方案是,可以在Bean定义中指定一个普通的初始化方法,然后在XML配置文件中通过指定init-method
属性来完成。如下面的定义所示:
<bean id="exampleInitBean" class="examples.ExampleBean" init-method="init"/>
public class ExampleBean {
public void init() {
// do some initialization work
}
}
…效果与下面完全一样…
<bean id="exampleInitBean" class="examples.AnotherExampleBean"/>
public class AnotherExampleBean implements InitializingBean {
public void afterPropertiesSet() {
// do some initialization work
}
}
析构回调
实现org.springframework.beans.factory.DisposableBean
接口的bean允许在容器销毁该bean的时候获得一次回调。DisposableBean
接口也只规定了一个方法:
void destroy() throws Exception;
通常,要避免使用DisposableBean
标志接口而且不鼓励使用该接口,因为这样会将代码与Spring耦合在一起,有一个可选的方案是,在bean定义中指定一个普通的析构方法,然后在XML配置文件中通过指定destroy-method
属性来完成。如下面的定义所示:
<bean id="exampleInitBean" class="examples.ExampleBean" destroy-method="cleanup"/>
public class ExampleBean {
public void cleanup() {
// do some destruction work (like releasing pooled connections)
}
}
…效果与下面完全一样…
<bean id="exampleInitBean" class="examples.AnotherExampleBean"/>
public class AnotherExampleBean implements DisposableBean {
public void destroy() {
// do some destruction work (like releasing pooled connections)
}
}
… 但是没有将代码与Spring耦合在一起。
注意:
spring也提供了两个接口来实现相同的功能:
InitializingBean和DisposableBean.
InitializingBean接口提供了一个afterPropertiesSet()方法。
DisposableBean接口提供了destroy().
不推荐使用该接口,它将你的bean和springAPI邦定在一起。
装配Bean
在spring容器内拼凑bean叫做装配。装配bean的时候,需要告诉容器哪些bean 以及容器如何使用依赖注入将它们配合在一起。
使用XML装配
xml是最常见的spring应用系统配置源。
几种spring容器都支持使用xml装配bean,包括:
- XmlBeanFactory:调用ClassPathResource载入上下文定义文件(比如applicationContext.xml)。
- ClassPathXmlApplicationContext:从类路径载入上下文定义文件。
- XmlWebApplicationContext:从web应用上下文中载入定义文件。
装配方式
上下文定义文件的根元素是.有多个子元素。每个元素定义了一个bean如何被装配到spring容器中。
<beans>
<bean id="foo" class="...Foo"/>
<bean id="bar" class="...Bar"/>
</beans>
对bean的最基本的配置包括bean的ID和他的全称类名。
继承装配
① 继承
② 继承配置
③ 覆盖父 Bean配置
④ 可以设置 的abstract 属性为 true, Spring 不会实例化该Bean
通过添加 parent 属性
<bean id="graduate" parent="student" class="com.inherit.Graduate">
实例
父类
public class Student {
private String name;
private int age;
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 class Graduate extends Student {
private String degree;
public String getDegree() {
return degree;
}
public void setDegree(String degree) {
this.degree = degree;
}
}
beans.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"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd">
<!-- 配置一个学生对象 -->
<bean id="student" class="com.inherit.Student">
<property name="name" value="Nick" />
<property name="age" value="25" />
</bean>
<!-- 配置子类 -->
<bean id="graduate" parent="student" class="com.inherit.Graduate">
<!-- 如果自己配置属性name,age,可以覆盖父类属性 -->
<property name="degree" value="master" />
<property name="name" value="Tom" />
</bean>
</beans>
测试类
public class App {
public static void main(String[] args) {
ApplicationContext ac = new ClassPathXmlApplicationContext("com/inherit/beans.xml");
Graduate graduate = (Graduate) ac.getBean("graduate");
System.out.println(graduate.getName());
System.out.println(graduate.getAge());
System.out.println(graduate.getDegree());
}
}
输出:
Tom //注意这里通过对子类属性的配置覆盖了父类属性,java继承的特性
25
master
自动装配Bean的属性值(重点)
Spring IoC容器可以自动装配(autowire)相互协作bean之间的关联关系。
* Autowiring modes*
模式 | 说明 |
---|---|
no | |
byName | 根据属性名自动装配。此选项将检查容器并根据名字查找与属性完全一致的bean,并将其与属性自动装配。例如,在bean定义中将autowire设置为by name,而该bean包含master属性(同时提供setMaster(..)方法),Spring就会查找名为master 的bean定义,并用它来装配给master属性。 |
byType | 如果容器中存在一个与指定属性类型相同的bean,那么将与该属性自动装配。如果存在多个该类型的bean,那么将会抛出异常,并指出不能使用byType方式进行自动装配。若没有找到相匹配的bean,则什么事都不发生,属性也不会被设置。如果你不希望这样,那么可以通过设置dependency-check="objects" 让Spring抛出异常。 |
constructor | 与byType的方式类似,不同之处在于它应用于构造器参数。如果在容器中没有找到与构造器参数类型一致的bean,那么将会抛出异常。 |
autodetect | 通过bean类的自省机制(introspection)来决定是使用constructor还是byType方式进行自动装配。如果发现默认的构造器,那么将使用byType方式。 |
如果直接使用property
和constructor-arg
注入依赖的话,那么将总是
覆盖自动装配。而且目前也不支持简单类型的自动装配,这里所说的简单类型包括基本类型、String
、Class
以及简单类型的数组(这一点已经被设计,将考虑作为一个功能提供)。byType和constructor自动装配模式也可用于数组和指定类型的集合。在这种情况下容器中的所有匹配的自动装配对象将被用于满足各种依赖。
使用说明
- byName的用法:
<!-- 配置一个master对象 -->
<bean id="master"class="com.hsp.autowire.Master" autowire="byName">
<property name="name">
<value>顺平</value>
</property>
</bean>
<!-- 配置dog对象 -->
<bean id="dog" class="com.hsp.autowire.Dog">
<property name="name"value="小黄"/>
<property name="age"value="3"/>
</bean>
byType: byType:寻找和属性类型相同的bean,找不到,装不上,找到多个抛异常。
constructor: autowire=”constructor”
说明 :查找和bean的构造参数一致的一个或多个bean,若找不到或找到多个,抛异常。按照参数的类型装配
- autodetect
说明 : autowire=”autodetect” (3)和(2)之间选一个方式。不确定性的处理与(3)和(2)一致。
- defualt
这个需要在
注入依赖
依赖注入(DI)背后的基本原理是对象之间的依赖关系(即一起工作的其它对象)只会通过以下几种方式来实现:构造器的参数、工厂方法的参数,或给由构造函数或者工厂方法创建的对象设置属性。也就是在创建Bean时通过容器向Bean中注入Bean与Bean之间的各种依赖关系
构造器注入
构造器参数解析
构造器参数解析根据参数类型进行匹配,如果bean的构造器参数类型定义非常明确,那么在bean被实例化的时候,bean定义中构造器参数的定义顺序就是这些参数的顺序,依次进行匹配,比如下面的代码
package x.y;
public class Foo {
public Foo(Bar bar, Baz baz) {
// ...
}
}
上述例子中由于构造参数非常明确(这里我们假定 Bar
和 Baz
之间不存在继承关系)。因此下面的配置即使没有明确指定构造参数顺序(和类型),也会工作的很好。
<beans>
<bean name="foo" class="x.y.Foo">
<constructor-arg>
<bean class="x.y.Bar"/>
</constructor-arg>
<constructor-arg>
<bean class="x.y.Baz"/>
</constructor-arg>
</bean>
</beans>
我们再来看另一个bean,该bean的构造参数类型已知,匹配也没有问题(跟前面的例子一样)。但是当使用简单类型时,比如<value>true<value>
,Spring将无法知道该值的类型。不借助其他帮助,他将无法仅仅根据参数类型进行匹配,比如下面的这个例子:
package examples;
public class ExampleBean {
// No. of years to the calculate the Ultimate Answer
private int years;
// The Answer to Life, the Universe, and Everything
private String ultimateAnswer;
public ExampleBean(int years, String ultimateAnswer) {
this.years = years;
this.ultimateAnswer = ultimateAnswer;
}
}
构造器参数类型匹配
针对上面的场景可以通过使用'type'
属性来显式指定那些简单类型的构造参数的类型,比如:
<bean id="exampleBean" class="examples.ExampleBean">
<constructor-arg type="int" value="7500000"/>
<constructor-arg type="java.lang.String" value="42"/>
</bean>
构造参数索引
我们还可以通过index
属性来显式指定构造参数的索引,比如下面的例子:
<bean id="exampleBean" class="examples.ExampleBean">
<constructor-arg index="0" value="7500000"/>
<constructor-arg index="1" value="42"/>
</bean>
通过使用索引属性不但可以解决多个简单属性的混淆问题,还可以解决有可能有相同类型的2个构造参数的混淆问题了,注意index是从0开始。
Setter方法注入
略
对比两种注入方式:
set注入的缺点是无法清晰表达哪些属性是必须的,哪些是可选的,构造注入的优势是通过构造强制依赖关系,不可能实例化不完全的或无法使用的bean。
对集合注入操作(set注入演示)
Bean类
public class Department {
private String name;
private String[] empName;
private List<Employee> emplist;
private Set<Employee> empSet;
private HashMap<Integer, Employee> empMaps;
public void setEmpMaps(HashMap<Integer, Employee> empMaps) {
this.empMaps = empMaps;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String[] getEmpName() {
return empName;
}
public void setEmpName(String[] empName) {
this.empName = empName;
}
public List<Employee> getEmplist() {
return emplist;
}
public void setEmplist(List<Employee> emplist) {
this.emplist = emplist;
}
public Set<Employee> getEmpSet() {
return empSet;
}
public void setEmpSet(Set<Employee> empSet) {
this.empSet = empSet;
}
public HashMap<Integer, Employee> getEmpMaps() {
return empMaps;
}
}
public class Employee {
private String name;
private int id;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
}
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"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd">
<bean id="department" class="com.collection.Department">
<property name="name" value="财务部"/>
<!-- 给数组注入值 -->
<property name="empName">
<list>
<value>小李</value>
<value>小王</value>
<value>小文</value>
<value>小张</value>
</list>
</property>
<!-- 给list注入值 -->
<property name="emplist">
<list>
<ref bean="emp1"/>
<ref bean="emp2" />
<ref bean = "emp3"/>
</list>
</property>
<!-- 给set注入值 -->
<property name="empSet">
<!-- 测试set集合的不重复特性-->
<set>
<ref bean="emp1"/>
<ref bean="emp2" />
<ref bean = "emp3"/>
<ref bean="emp2" />
<ref bean = "emp3"/>
<ref bean="emp2" />
<ref bean = "emp3"/>
<ref bean="emp2" />
<ref bean = "emp3"/>
<ref bean="emp2" />
<ref bean = "emp3"/>
</set>
</property>
<!-- 给map注入值 -->
<property name="empMaps">
<map>
<entry key="1" value-ref="emp1"/>
<entry key="2" value-ref="emp2"/>
<entry key="3" value-ref="emp3"/>
</map>
</property>
</bean>
<bean id="emp1" class="com.collection.Employee">
<property name="name" value="北京" />
<property name="id" value="1" />
</bean>
<bean id="emp2" class="com.collection.Employee">
<property name="name" value="深圳" />
<property name="id" value="2" />
</bean>
<bean id="emp3" class="com.collection.Employee">
<property name="name" value="上海" />
<property name="id" value="3" />
</bean>
</beans>
测试类
package com.collection;
import java.util.Map.Entry;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class App {
public static void main(String[] args) {
ApplicationContext ac = new ClassPathXmlApplicationContext("com/collection/beans.xml");
Department department = (Department) ac.getBean("department");
System.out.println(department.getName());
System.out.println("雇员名称");
for (String empName : department.getEmpName()) {
System.out.println(empName);
}
System.out.println("*******通过list集合取出数据******");
for (Employee employee : department.getEmplist()) {
System.out.println(employee.getName());
}
System.out.println("*******通过set集合取出数据******");
for (Employee employee : department.getEmpSet()) {
System.out.println(employee.getName());
}
System.out.println("*******通过map集合取出数据******");
for (Entry<Integer, Employee> entry : department.getEmpMaps().entrySet()) {
System.out.println("key: " + entry.getKey() + ", value = " + entry.getValue().getName());
}
}
}
输出结果:
财务部
雇员名称
小李
小王
小文
小张
*******通过list集合取出数据******
北京
深圳
上海
*******通过set集合取出数据******
北京
深圳
上海
*******通过map集合取出数据******
key: 1 vule = 北京
key: 2 vule = 深圳
key: 3 vule = 上海
使用Spring的特殊Bean
让spring特殊对待这些bean。使它们可以:
通过配置后加工bean,涉及到Bean和Bean工厂生命周期。
2.改变依赖注入,将字符串转换成其它类型。
3.从属性文本装载信息,包括信息国际化。
4.监听并处理其它bean及spring发布的系统消息。
5.知道自己在spring中的唯一表识。
对bean工厂进行后处理
分散配置(有两种方式引入文件)
将配置文件分成几个分散的配置文件。如数据源
<bean class="...PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>xx/yy/db.properties</value>
<value>xx/yy/db2.properties</value>
</list>
</property>
</bean>
<!– 名字空间配置(2.5) -->
<context:property-placeholder location="classpath:com/hsp/spring/dispatcher/db.properties"/>
感知其他bean
运行在spring容器中的bean不知道自己的注册名,运行在哪里。实现以下三个接口:
BeanNameAware:知道自己的名字。
无需为该接口的setBeanName()方法作任何处理,bean被载入时,容器会自动调用该方法,为其设置id或name的值。
BeanFactoryAware:所处的bean工厂。
ApplicationContextAware:所在上下文