spring是什么?
struts 是 web 框架 (jsp/action/actionfrom)
hibernate 是 orm框架,处于持久层.
spring 是容器框架,用于配置bean,并维护bean之间关系的框架
spring中有一个非常概念:bean (是java中的任何一种对象 javabean/service/action/数据源./dao, ioc(控制反转 inverseof control) di( dependency injection 依赖注入)
ioc或者说di,还可以达到解耦的目的
快速入门
开发一个spring项目.
1. 引入spring的开发包(最小配置spring.jar该包把常用的jar都包括, 还要写日志包 common-logging.jar
2. 创建spring的一个核心文件applicationContext.xml, [hibernate有核心 hibernate.cfg.xmlstruts核心文件 struts-config.xml], 该文件一般放在src目录下,该文件中引入 xsd文件:
可以从给出的案例中拷贝一份.
3. 配置bean
<!-- 在容器文件中配置bean(service/dao/domain/action/数据源) -->
<!-- bean元素的作用是,当我们的spring框架加载时候,spring就会自动的创建一个bea对象,并放入内存
UserServiceuserSerivce=new UserService();
userSerivce.setName("韩顺平");
-->
<bean id="userService"class="com.service.UserService">
<!-- 这里就体现出注入的概念.-->
<propertyname="name">
<value>韩顺平</value>
</property>
</bean>
4. 在Test.java中,我们怎么使用
//我们现在使用spring来完成上面的任务
//1.得到spring 的applicationContext对象(容器对象)
ApplicationContextac=new ClassPathXmlApplicationContext("applicationContext.xml");
UserServiceus=(UserService) ac.getBean("userService");
us.sayHello();
5. 细节讨论?
传统的方法和使用spring的方法
5.1 使用spring ,没有new 对象,我们把创建对象的任务交给spring框架
8.2 spring的运行原理图:
//spring 实际上是一个容器框架,可以配置各种bean(action/service/domain/dao),并且可以维护bean与bean之间的关系,当我们需要使用某个bean的时候,我们可以使用getBean(id);
//ioc是什么?
答 :ioc(inverse ofcontroll ) 控制反转: 所谓控制反转就是把创建对象(bean),和维护对象(bean)的关系的权利从程序中转移到spring的容器(applicationContext.xml),而程序本身不再维护.
//DI是什么?
答: di(dependencyinjection) 依赖注入: 实际上di和ioc是同一个概念,spring设计者认为di更准确表示spring核心技术
//学习框架,最主要的是学习各个配置
//spring 开发者提倡接口编程,配合di技术可以实现层与层的解耦
创建一个接口 ChangeLetter
两个类实现接口
把对象配置到spring容器中
使用
//通过上面的案例,我们可以初步体会到di配合接口编程,的确可以减少层(web层) 和 业务层的耦合度
8.3 Bean
从ApplicationContext 应用上下文容器中获取bean和从bean工厂容器中获取bean
ApplicationCotext,spring更加高级的容器。功能
强大:
1.提供文本信息解析工具,包括对国际化支持。
2.提供载入文件资源的通用方法,如图片。
3.可以向注册为监听器的bean发送事件。
在很少的情况下,使用BeanFactory,如在移动设
备。
三种经常用到的实现:
1.ClassPathXmlApplicationContext:从类路径中加载。
2.FileSystemXmlApplicationContext:从文件系统加载。
3.XmlWebApplicationContext:从web系统中加载。
ApplicationContext context = new FileSystemXmlApplicationContext(" c:\foo.xml");
ApplicationContext context = new ClassPathXmlApplicationContext(" foo.xml");
具体案例:
//从ApplicationContext中取bean
ApplicationContextac=new ClassPathXmlApplicationContext("com/hsp/ioc/beans.xml");
//当我们去实例化beans.xml,该文件中配置的bean被实例(该bean scope是 singleton)从bean中取出student
//如果我们使用beanfactory去获取bean,当你只是实例化该容器, 那么
//容器的bean不被实例化,只有当你去使用getBean某个bean时,才会实时的创建.
BeanFactoryfactory = new XmlBeanFactory( newClassPathResource("com/hsp/ioc/beans.xml"));
factory.getBean("student");
结论:
1.如果使用ApplicationContext,则配置的bean如果是 singlton不管你用不用,都被实例化.(好处就是可以预先加载,缺点就是耗内存)
2.如果是 BeanFactory ,则当你获取beanfacotry时候,配置的bean不会被马上实例化,当你使用的时候,才被实例(好处节约内存,缺点就是速度)
3.规定: 一般没有特殊要求,应当使用ApplicatioContext完成(90%)
u bean 的 scope的细节
bean的生命周期
1. 实例化(当我们的程序加载beans.xml文件),把我们的bean(前提是scope=singleton)实例化到内存
2. 调用set方法设置属性
3. 如果你实现了bean名字关注接口(BeanNameAware) 则,可以通过setBeanName获取id号
4. 如果你实现了 bean工厂关注接口,(BeanFactoryAware),则可以获取BeanFactory
5. 如果你实现了ApplicationContextAware接口,则调用方法
//该方法传递ApplicationContext
public voidsetApplicationContext(ApplicationContext arg0)
throwsBeansException {
// TODOAuto-generated method stub
System.out.println("setApplicationContext"+arg0);
}
6. 如果bean 和一个后置处理器关联,则会自动去调用Object postProcessBeforeInitialization方法
7. 如果你实现InitializingBean 接口,则会调用afterPropertiesSet
8. 如果自己在<bean init-method=”init” /> 则可以在bean定义自己的初始化方法.
9. 如果bean 和一个后置处理器关联,则会自动去调用Object postProcessAfterInitialization方法
10. 使用我们的bean
11. . 容器关闭
12. 可以通过实现DisposableBean接口来调用方法 destory
13. 可以在<bean destory-method=”fun1”/> 调用定制的销毁方法
小结: 我们实际开发中往往,没有用的这么的过程,常见的是:
1->2->6->10->9->11
问题:通过BeanFactory来获取bean对象,bean的生命周期是否和Applicationcontext 是一样吗?
不是一样的,bean是工厂中创建的生命周期会简单一些:
除了应用上下文提供的附加功能外,应用上下文
与bean工厂的另一个重要区别是关于单例bean
如何被加载。
bean工厂延迟加载所有bean,直到getBean()
方法被调用。
应用上下文会(applicationContext)在启动后预载入所有单例bean.这样可确保应用不需要等待他们被创建。
*应用上下文容器,在启动后,只会预加载所有的单例bean ,如果bean 的scope=“prototype|request|session”,并不会预加载,而是交给程序去维护
配置bean的细节
Scope的说明
☞ 尽量使用 scope=”singleton”,不要使用prototype,因为这样对我们的性能影响较大.
<bean scope=“singleton|prototype|request|session” />
singleton: 单例 默认值
prototype: 原型
request: 一次请求有效( javaweb开发中)
session: session级有效(java web开发中)
bean工厂介绍
工厂设计模式,创建分发各种bean。配置好它们之
间的写作关系,参与bean的生命周期。
BeanFactory factory = new XmlBeanFactory(
newClassPathResource("applicationContext.xml"));
bean工厂只把bean的定义信息载进来,用到的时候
才实例化。
factory.getBean("mybean");就可得到一个
bean。
bean工厂:最简单的容器,提供了基础的依赖
注入支持。创建各种类型的Bean.
应用上下文(ApplicationContext):建立在bean工厂基础之上,提供系统架构服务。
1.4 装配bean
bean工厂:最简单的容器,提供了基础的依赖 注入支持。创建各种类型的Bean.
应用上下文(ApplicationContext):建立在bean工厂基础之上,提供系统架构服务
1.4.1 beanFactory
<bean scope=“singleton|prototype|request|session” />
singleton: 单例默认值
prototype: 原型
request: 一次请求有效( java web开发中)
session: session级有效(java web开发中)
1 bean工厂介绍
工厂设计模式,创建分发各种bean。配置好它们之间的写作关系,参与bean的生命周期。
BeanFactory factory = new XmlBeanFactory(newClassPathResource("applicationContext.xml"));
bean工厂只把bean的定义信息载进来,用到的时候才实例化。
factory.getBean("mybean");就可得到一个bean。
8.4.3 bean的生命周期
bean被载入到容器中时,他的生命周期就开始了。:
1.容器寻找bean的定义信息并实例化。
2.使用依赖注入,spring按bean定义信息配置bean的所有属性。
3.若bean实现了BeanNameAware接口,工厂调用Bean的setBeanName
()方法传递bean的ID。
4.若bean实现了BeanFactoryAware接口,工厂调用setBeanFactory()
方法传入工厂自身。
5.若BeanPostProcessor(bean后置处理器)和bean关联,则它们的
postProcessBeforeInitialization()方法被调用。 [过滤器举例理解]
6.若bean指定了ini-method方法、,它将被调用。
7.最后,若有BeanPostProcessor和bean关联,则它们的
postProcessAfterInitialization()方法被调用、。
将bean从工厂中删掉有两种方法:
1.若bean实现了DisposableBean接口, distroy()方法被调用。
2.如果指定了定制的销毁方法,就调用这个方法
1.5 Bean基本装配
装配bean的时候,需要告诉容器哪些bean以及容器如何使用依赖注入将它们配合在一起。
使用XML装配
几种spring容器都支持使用xml装配bean,包括:
1.XmlBeanFactory:调用ClassPathResource载入上下文
定义文件(比如applicationContext.xml)。
2.ClassPathXmlApplicationContext:从类路径载
入上下文定义文件。
3.XmlWebApplicationContext:从web应用上下文
中载入定义文件。
上下文定义文件的根元素是<beans>.<beans>有多
个<bean>子元素。每个<bean>元素定义了一个
bean如何被装配到spring容器中。
<beans>
<bean id="foo"class="...Foo"/>
<bean id="bar"class="...Bar"/>
</beans>
对bean的最基本的配置包括bean的ID和他的全称类名。
<bean id="foo"class="...Foo"/>
bean的id是foo。
基本装配-scope
<bean scope=“singleton|prototype|request|session” />
prototype、singleton、request 、session、global-session spring中的bean缺省情况下是单例模式。始终返回一个实例。若想返回不同的实例的话需要定义成原型模式。bean的singleton属性告诉上下文该bean是否为单例的。缺
省为true。若为false的话,为原型bean。
<beanid="foo" class="...Foo" singleton="false"/>
<!– spring2.5 -->
<beanscope="prototype|single|..">
使用原型bean会对性能产生影响,尽量不要设置为prototype,除非有必要。
实例化与销毁
spring实例化bean或销毁bean时,有时需要作一些处理工作,因此spring可以在创建和拆卸bean的时候调用bean的两个生命周期方法。
<bean class="Foo" init-method destory-method>
<!-- spring2.5-->
@PostConstruct public void ini(){…}
@PreDestroy public void destroy(){…}
<bean class="...CommonAnnotationBeanPostProcessor">
spring也提供了两个接口来实现相同的功能:
InitializingBean和DisposableBean.
InitializingBean接口提供了一个afterPropertiesSet()方法。
DisposableBean接口提供了destroy().
不推荐使用该接口,它将你的bean和springAPI邦定在一起。
通过set方法注入依赖
<bean>元素的< property >子元素指明了使用它们的set方法来注入。可以注入任何东西,从基本类型到集合类,甚至是应用系统的bean。
简单bean配置
配置bean的简单属性,基本数据类型和string。
<bean id="foo" class="...Foo">
<property name="name">
<value>tom</value>
</property>
</bean>
引用其它bean
<bean id="foo"class="...Foo">
<property name="name">
<ref bean="bar">
</property>
</bean>
<bean id="bar"class="...Bar">
</bean>
内部bean
<bean id="foo"class="...Foo">
<propertyname="bar">
<beanclass="...Bar">
</property>
</bean>
这种方式的缺点是你无法在其它地方重用这个bar实
例,原因是它是专门为foo而用。
<bean id=”foo” class=”....Foo”>
<property name=”属性”>
<!—第一方法引用-->
<ref bean=’bean对象名’/>
<!—内部bean-->
<bean>
<properyt></property>
</bean>
</property>
</bean>
继承
继承配置
覆盖父 Bean配置
可以设置 <bean> 的abstract 属性为 true, Spring 不会实例化该Bean
<bean id="gradate" parent="student"
class=“...Gradate">
Student (protected String name,private intage) Graduate( protected String degree)
<!-- 继承配置 -->
<bean id="student"class="com.hsp.spring.inherit.Student">
<property name="name"value="小明" />
<property name="age"value="12"/>
</bean>
<!-- graduate对象 -->
<bean id="gradate" parent="student" class="com.hsp.spring.inherit.Gradate">
<property name="degree"value="学士"/>
<property name="name"value="大明" />
</bean>
public class Student
public class Gradate extends Student
在beans.xml文件中体现配置
<!-- 配置一个学生对象 -->
<bean id="student"class="com.hsp.inherit.Student">
<propertyname="name" value="顺平" />
<propertyname="age" value="30"/>
</bean>
<!-- 配置Grdate对象 -->
<bean id="grdate"parent="student" class="com.hsp.inherit.Gradate">
<!--如果自己配置属性name,age,则会替换从父对象继承的数据 -->
<propertyname="name" value="小明"/>
<propertyname="degree" value="学士"/>
</bean>
Spring提供set方式给 bean注入值,还可以通过构造函数注入值
如何给集合类型注入值
Java中主要的集合有几种:map set list /数组
Map
<property name="barlist">
<map>
<entry key="key1" value="bar1" />
<entry key="key2 value-ref="xxx" />
</map>
</property>
key值必须是string的,key-ref可以是其他bean
Set
装配set:
<property name="barlist">
<set>
<refbean="bar2"/>
</set>
</property>
set使用方法和list一样,不同的是对象被装配到set
中,而list是装配到List或数组中装配
List
若bean的属性是集合类型,按如下处理:
装配List和数组:
<property name="barlist">
<list>
<value>bar1</value>
<ref bean="bar2"/>
</list>
</property>
Properties
装配Properties:
<property name="barlist">
<props>
<prop key="key1">bar1</prop>
<prop key="key2">bar2</prop>
</props>
</property>
设置null:
<property name="barlist">
<null/>
</property>
Department类:
package com.hsp.collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class Department {
private String name;
private String []empName;
privateList<Employee> empList;
privateSet<Employee> empsets;
privateMap<String,Employee> empMaps;
publicSet<Employee> getEmpsets() {
returnempsets;
}
public void setEmpsets(Set<Employee>empsets) {
this.empsets= empsets;
}
public String[]getEmpName() {
returnempName;
}
public voidsetEmpName(String[] empName) {
this.empName= empName;
}
public StringgetName() {
return name;
}
public void setName(Stringname) {
this.name =name;
}
publicList<Employee> getEmpList() {
returnempList;
}
public voidsetEmpList(List<Employee> empList) {
this.empList= empList;
}
public Map<String,Employee> getEmpMaps() {
returnempMaps;
}
public voidsetEmpMaps(Map<String, Employee> empMaps) {
this.empMaps= empMaps;
}
}
//Employeel类
package com.hsp.collection;
public class Employee {
private String name;
private int id;
public int getId() {
return id;
}
public void setId(intid) {
this.id =id;
}
public StringgetName() {
return name;
}
public voidsetName(String name) {
this.name =name;
}
}
beans.xml配置文件:
<?xml version="1.0" encoding="utf-8"?>
<beansxmlns="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/beanshttp://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context-2.5.xsd
http://www.springframework.org/schema/txhttp://www.springframework.org/schema/tx/spring-tx-2.5.xsd">
<bean id="department"class="com.hsp.collection.Department">
<property name="name"value="财务部"/>
<!-- 给数组注入值 -->
<propertyname="empName">
<list>
<value>小明</value>
<value>小明小明</value>
<value>小明小明小明小明</value>
</list>
</property>
<!-- 给list注入值 list中可以有相当的对象 -->
<propertyname="empList">
<list>
<ref bean="emp2" />
<ref bean="emp1"/>
<ref bean="emp1"/>
<ref bean="emp1"/>
<ref bean="emp1"/>
<ref bean="emp1"/>
<ref bean="emp1"/>
</list>
</property>
<!-- 给set注入值 set不能有相同的对象 -->
<propertyname="empsets">
<set>
<ref bean="emp1" />
<ref bean="emp2"/>
<ref bean="emp2"/>
<ref bean="emp2"/>
<ref bean="emp2"/>
</set>
</property>
<!-- 给map注入值 map只有key不一样,就可以装配value -->
<propertyname="empMaps">
<map>
<entry key="11"value-ref="emp1" />
<entry key="22"value-ref="emp2"/>
<entry key="33"value-ref="emp1"/>
</map>
</property>
<!-- 给属性集合配置 -->【点http协议 referer】
<propertyname="pp">
<props>
<prop key="pp1">abcd</prop>
<prop key="pp2">hello</prop>
</props>
</property>
</bean>
<bean id="emp1"class="com.hsp.collection.Employee">
<property name="name"value="北京"/>
<property name="id"value="1"/>
</bean>
<bean id="emp2"class="com.hsp.collection.Employee">
<property name="name"value="天津"/>
<property name="id"value="2"/>
</bean>
</beans>
通过构造函数注入值
set注入是一种直接方式,也可通过构造函数设置一些属性值。
*使用Employee (String name,int age, double salary)
packagecom.hsp.spring.constructor;
publicclass Employee {
private String name;
private int age;
private double salary;
public Employee(String name, int age) {
System.out.println("ok2");
this.name = name;
this.age = age;
}
//一个无参的构造函数.
public Employee(){
}
//通过构造函数注入值
public Employee(String name, int age,double salary) {
System.out.println("ok1");
this.name = name;
this.age = age;
this.salary = salary;
}
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 double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
}
<constructor-arg index="0"type="java.lang.String" value="顺平" />
<constructor-argindex="1" type="int" value="20"/>
<constructor-arg index="2" type="double"value="34.5" />
set注入的缺点是无法清晰表达哪些属性是必须的,哪些是可选
的,构造注入的优势是通过构造强制依赖关系,不可能实例化不
完全的或无法使用的bean。
☞ :如果属性是类类型,则使用 ref=“”
<!--配置一个Employee对象 | 完成同样事情,提供几个方法, -->
<beanid="employee" class="com.hsp.spring.constructor.Employee">
<constructor-argindex="0" type="java.lang.String"value="shunping" />
<constructor-argindex="1" type="int" value="20" />
<constructor-argindex="2" type="double" value="56.67" />
</bean>
beans.xml 关键代码:
<!-- 配置一个雇员对象 -->
<bean id="employee" class="com.hsp.constructor.Employee">
<!-- 通过构造函数来注入属性值 -->
<constructor-arg index="0"type="java.lang.String" value="大明" />
</bean>
1.4 自动装配bean的属性值
<beanid="foo" class="...Foo" autowire="autowiretype">
有四种自动装配类型:
1.byName寻找和属性名相同的bean,若找不到,则装不上。
2.byType:寻找和属性类型相同的bean,找不到,装不上,找到多个抛异常。
3.constructor:查找和bean的构造参数一致的一个或
多个bean,若找不到或找到多个,抛异常。按照参数的类型装配
4.autodetect: (3)和(2)之间选一个方式。不确定
性的处理与(3)和(2)一致。
5.defualt : 这个需要在<beans defualt-autorwire=“指定” />
6.no : 不自动装配,这是autowrite的默认值.
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>
1 byType: byType:寻找和属性类型相同的bean,找不到,装不上,找到多个抛异常。
3 constructor: autowire="constructor"
说明 : 查找和bean的构造参数一致的一个或
多个bean,若找不到或找到多个,抛异常。按照参数的类型装配
4 autodetect 说明:autowire=“autodetect” 3和2之间选取一个方式,不确定性的处理方式3和 2一致
5 default
这个需要在<beans defualt-autorwire=“指定” />
当你在<beans >指定了 default-atuowrite后,所有的bean的默认的autowire就是指定的装配方法;
如果没有在<beans defualt-autorwire=“指定” /> 没有 defualt-autorwire=“指定” ,则默认是
defualt-autorwire=”no”
6 no:不自动装配
使用byType和constructor自动装配时,若找到多个符合条件的bean,会报异常,因此最好的方式是不用自动组装。
混合使用手动和自动组装
<bean id="bar"class="...Bar"
autowire="byName">
<property name="cousedao">
<ref bean="somebean" />
</property>
</bean>
spring2.5
spring2.5提供了<context:annotation-config />配置.
该配置可激活在类中探测到的各种注解,@Required @Autowire @PostConstrct @PreDestroy @Resource @EJB@PersistenceContext @WebServiceRef等等,
也可以选择为这些注解激活单独的后处理器.
例如:AutowiredAnnotationBeanPostProcessor(处理自动装配)
CommonAnnotationBeanPostProcessor(生命周期等)
注意:该标记不能激活事务处理.需要使用tx:annotation-driven
这个知识点很有用,在项目中,我们再具体使用。
缺省自动组装
默认不是自动组装。
通过<beansdefault-
autowire="byName">
可将bean设置为自动组装。
8.7 使用spring的特殊bean完成分散配置
让spring特殊对待这些bean。使它们可以:
1.通过配置后加工bean,涉及到Bean和Bean工厂生命
周期。
2.改变依赖注入,将字符串转换成其它类型。
3.从属性文本装载信息,包括信息国际化。
4.监听并处理其它bean及spring发布的系统消息。
5.知道自己在spring中的唯一表识。
BeanPostProcessor接口提供机会来修改bean。
public interface BeanPostProcessor{
//Bean初始化(调用afterPropertiesSet()以及Bean的指
//定initmethod方法)之前被调用。
Object postProcessorBeforeInitialation(…);
//在初始化之后马上调用
Object postProcessorAfterInitialization(…);
}
注册后处理Bean
如果使用的是Bean工厂,需要调用工厂的addBeanPostProcessor()来注册.
factory.addBeanPostProcessor(…);
如果使用的是上下文同其他Bean一样:
<bean id="" class="……" />
对bean工厂进行后处理
BeanPostProcessor在bean载入后,对bean进行一些后处理工作。而BeanFactoryPostProcessor在bean工厂载入所有bean的定义后,实例化bean之前,对Bean工厂做一些后处理工作。
public interface BeanFactoryPostProcessor{
public voidpostProcessorBeanFactory(…);
}
如果使用的是应用上下文不需要将他注册为Bean工厂后处理器。上下文会自动注册它。按照正常的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-placeholderlocation="classpath:com/hsp/spring/dispatcher/db.properties"/>
DBUtil (private String username;privateString password;private String url;private String drivername;)
jdbc.properties
database.url=
database.driver=
datebase.username=
datebase.password=
使用占位符变量代替bean装配文件中的硬编码配置。占位符采
${variable}形式。
<bean id="datasource"
class="...DriverManagerDataSource">
<property name="url">
<value>${database.url}</value>
</property>
......
</bean>
beans.xml
说明: 当通过context:property-placeholder 引入 属性文件的时候,有多个需要使用 , 号间隔.
<!-- 引入我们的db.properties文件 -->
<context:property-placeholderlocation="classpath:com/hsp/dispatch/db.properties,classpath:com/hsp/dispatch/db2.properties"/>
<!-- 配置一DBUtil对象 $占位符号 -->
<bean id="dbutil"class="com.hsp.dispatch.DBUtil">
<property name="name" value="${name}" />
<property name="drivername"value="${drivername}" />
<property name="url" value="${url}" />
<property name="pwd" value="${pwd}" />
</bean>
<!-- 配置一DBUtil对象 -->
<beanid="dbutil2" class="com.hsp.dispatch.DBUtil">
<property name="name" value="${db2.name}"/>
<property name="drivername"value="${db2.drivername}" />
<property name="url" value="${db2.url}" />
<property name="pwd" value="${db2.pwd}" />
</bean>
db.properties:
name=scott
drivername=oracle:jdbc:driver:OracleDirver
url=jdbc:oracle:thin:@127.0.0.1:1521:hsp
pwd=tiger
感知其他bean
运行在spring容器中的bean不知道自己的注册名,运行在哪里。实现以下三个接口:
BeanNameAware:知道自己的名字。
BeanFactoryAware:所处的bean工厂。
ApplicationContextAware:所在上下文
实现BeanNameAware接口。无需为该接口的setBeanName()方法作任何处理,bean被载入时,容器会自动调用该方法,为其设置id或name的值。
实现BeanFactoryAware(感知)或ApplicationContextAware接口。分别定义了setBeanFactory()和setApplicationContext()方法。
1.4 aop编程
3.1.1 定义AOP术语
1.切面(aspect):要实现的交叉功能,是系统模块化的一个切面或领域。如日志记录。
2.连接点:应用程序执行过程中插入切面的地点,可以是方法调用,异常抛出,或者要修改的
字段。
3.通知:切面的实际实现,他通知系统新的行为。如在日志通知包含了实
现日志功能的代码,如向日志文件写日志。通知在连接点插入到应用系统中。
4.切入点:定义了通知应该应用在哪些连接点,通知可以应用到AOP框架支持的任何连接点。
5.引入:为类添加新方法和属性。
6.目标对象:被通知的对象。既可以是你编写的类也可以是第三方类。
7.代理:将通知应用到目标对象后创建的对象,应用系统的其他部分不用为了支持代理对象而
改变。
8.织入:将切面应用到目标对象从而创建一个新代理对象的过程。织入发生在目标
对象生命周期的多个点上:
编译期:切面在目标对象编译时织入.这需要一个特殊的编译器.
类装载期:切面在目标对象被载入JVM时织入.这需要一个特殊的类载入器.
运行期:切面在应用系统运行时织入.
SpringAOP实现
用java编写spring通知
在spring中所有的通知都是以java类的形式编写的。切入点定义在配置文件中编写,所以切面代码和配置文件对我们来说都很熟悉。
对于其他框架(Aspectj),需要特定的语法编写,如果使用的话,还需学习新的语言。
spring的运行时通知对象
spring在运行期创建代理,不需要特殊的编译器.
spring有两种代理方式:
1.若目标对象实现了若干接口,spring使用JDK的
java.lang.reflect.Proxy类代理。该类让spring动态产生
一个新类,它实现了所需的接口,织入了通知,并且代理对
目标对象的所有请求。
2.若目标对象没有实现任何接口,spring使用
CGLIB库生成目标对象的子类。使用该方式时需要注意:
1.对接口创建代理优于对类创建代理,因为会产生更加松耦合的系统。
对类代理是让遗留系统或无法实现接口的第三方类库同样可以得到通知,
这种方式应该是备用方案。
2.标记为final的方法不能够被通知。spring是为目标类产生子类。任何需要
被通知的方法都被复写,将通知织入。final方法是不允许重写的。
spring实现了aop联盟接口。
spring只支持方法连接点:不提供属性接入点,spring的观点是属性拦截破坏了
封装。面向对象的概念是对象自己处理工作,其他对象只能通过方法调用的得到的
结果。
创建通知
前置通知
public interface MethodBeforeAdvice{
void before(Method m,Object[]
os ,Object target){
}
}
该接口提供了获得目标方法、参数和目标对象的机会。不能够改变运行时参数,即不能替换参数对象和目标对象。
注意在方法结束后不返回任何值东西。原因是该接口返回后,目标方法将会被调用,应该返回目标对象的返回值。该接口唯一能
阻止目标方法被调用的途径是抛出异常或(System.exit())。
public class WelcomeAdvice implementsMethodBeforeAdvice{
public void before(Method m,Object[] os,Object target){
Customer c = (Customer)arg[0];
System.out.println(c.getName());
}
}
---------------------------------------------
//创建目标代理对象
<bean id="kwikEMartTarget"class="…ApuKwikEMart" />
<bean id="welcomeAdvice"class="…WelcomeAdvice" />
<bean id="kwikEMart"class="……ProxyFactoryBean">
<property name="proxyInterfaces"><value>……KwikEMart</value></property>
<property name="interceptorNames">
<list><value>welcomeAdvice</value></list>
</property>
<property name="target"><refbean="kwikEMartTarget" /></property>
</bean>
ProxyFactoryBean是一个在BeanFactory中显式创建代理对象的中心类,可以给它一个
要实现的接口、一个要代理的目标对象、一个
要织入的通知,并且他将创建一个崭新的代理
对象。
后置通知
同前置通知类似。
public interface AfterReturningAdvice{
public void afterReturning(Object returnValue,Method
m,Object[] os,Object target);
}
环绕通知
public interface MethodInterceptor extendsInterceptor{
Object invoke(MethodInvocation invocation);
}
该接口同前两种通知有两个重要区别:1.该通知能够控制目标方法
是否真的被调用。通过invocation.proceed()方法来调用。
2.该通知可以控制返回的对象。可以返回一个与proceed()方法返回对象完全不同的对象。但要谨慎使用。
异常通知
public interface ThrowsAdvice{
}
该接口为标识性接口,没有任何方法,但实现该接口的类必须要有如下形
式的方法:
void afterThrowing(Throwable throwable);
void afterThrowing(Method m,Object[]os,Object
target,Exception throwable);
第一个方法只接受一个参数:需要抛出的异常。
第二个方法接受异常、被调用的方法、参数以及目标对象。
* Throwable throwable 这里可以具体使用Exceptione
引入通知
以前定义的通知类型是在目标对象的方法被调用
的周围织入。引入通知给目标对象添加新的方法
和属性。
定义切入点
如果不能表达在应用系统的什么地方应用
通知的话,通知将毫无用处,这就是切入点的用处。切入点决定了一个特定的类的特定方法是否满足一定的规则。若符合,通知就应用到该方法上。
<beanid="xxxTarget" class="xxxServiceImpl"/><beanid="xxxAdvice" class=""/>
<beanid="xxxAdvisor" class="…NameMatchMethodPointcutAdvisor">
<propertyname="mappedName">
<value>order*<value>
</property>
<property name="advice">
<refbean="xxxAdvice" />
</property>
</bean>
<beanid="xxxService" class="…ProxyFactoryBean">
<propertyname="proxyInterfaces">
<value>……xxxService</value>
</property>
<propertyname="interceptorNames">
<list><value>xxxAdvisor</value></list>
</property>
<propertyname="target"><ref bean="xxxTarget"/></property>
</bean>
正则表达式切入点
使用spring的静态切入点(续)正则表达式切入点
RegexpMethodPointcut
aop( aspect oriented programming ) 面向切面(方面)编程,是对所有对象或者是一类对象编程,核心是( 在不增加代码的基础上,还增加新功能 )
汇编(伪机器指令 mov jump) 面向机器
c语言(面向过程 )->系统软件(操作系统,数据库, 杀毒软件,防火墙,驱动..)
语句1;
语句2;
...
java语法(面向对象->类-对象)
class Dog{
属性;->变量
行为->函数
}
面向切面 spring( ->aop) 面向n多对象编程
aop特别提醒: aop编程,实际上在开发框架本身用的多,在实际项目中,用的不是很多,但是将来会越来越多,这个一个趋势.
编程说明
步骤:
1. 定义接口
2. 编写对象(被代理对象=目标对象)
3. 编写通知(前置通知目标方法调用前调用)
4. 在bean.xml文件配置
a) 配置 被代理对象=目标对象
b) 配置通知
c) 配置代理对象 是ProxyFactoryBean的对象实例
1 代理接口集
2 织入通知
3 配置被代理对象
后面还后置通知,环绕通知,异常通知,引入通知
在spring的aop中,当通过代理对象去实现aop的时候,获取的ProxyFactoryBean是什么类型
答:返回的是一个代理对象,如果目标实现了接口,则spring使用jdk动态代理技术,
如果目标没有实现接口,则spring使用CGLIB技术
class A{
private Strinag name;
public viodsetName(String name){
this.name=name;
System.out.println(“name”+name);
}
}
beans.xml
<bean id=”a” class=”...A”>
<property name=”name” value=”等待” />
</bean>
A a=new A();
a.setName(“等待”);