1.1.1 Spring简介
Spring是一个开源的控制反转(IoC)和面向且切面(AOP)的容器框架。
IOC控制反转:应用本身不负责以来对象的创建和维护,以来对象的创建及维护由外部容器负责。这样控制权就转移到了外部容器。
Dependency Injection依赖注入:在运行期,由外部容器动态地将以来对象注入到组件中。
事务控制全部交给spring处理,不用手工编写事务的创建和提交
1.1.2 Spring配置和搭建
Eclipse下的配置
下载了spring-framework-2.5.6包,把dist文件夹下的spring.jar添加到工程,还要添加一个jar是common-logging的,在hibernate学习的时候下载过了,也添加进去。
在src目录下新建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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
</beans>
初始化spring容器
ApplicationContext context = new ClassPathXmlApplicationContext( new String[] {"services.xml", "daos.xml"});//可以通过数组传递多个配置文件。
下面使用JUnit4进行测试,new->JUnit Test
(不要勾选setUpBeforeClass)
SprintTest.java
SprintTest.java
package junit.test;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class SpringTest {
@Test
public void instanceSpring(){
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
}
}
运行以后测试成功
下面写一个bean
PersonService.java 放在src中service包下
package service.impl;
public interface PersonService {
public abstract void save();
}
PersonServiceBean.java 放在src中service.impl包下
package service;
import service.impl.PersonService;
public class PersonServiceBean implements PersonService {
/* (non-Javadoc)
* @see service.impl.PersonService#save()
*/
public void save(){
System.out.println("save方法");
}
}
下面配置beans.xml
在没联网的情况下需要添加提示文件才会出现关键字提示。
windows->perferences->XML->XML Catalog
add
Location:在spring包下找到dist/resources/spring-beans-2.5.xsd
Key Type:Namespace Name
Key:http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
完成添加。
在beans标签中间添加
<bean id="personService" class="service.PersonServiceBean"></bean>
使用bean标签的时候,id和name的区别:id不可以包含特殊字符,name可以。
SpringTest.java
package junit.test;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import service.impl.PersonService;
public class SpringTest {
@Test
public void instanceSpring(){
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
PersonService personService = (PersonService)context.getBean("personService");
personService.save();
}
}
运行成功
1.1.3 Spring管理bean的原理
简单的一套管理bean的代码,就是一个模拟的ApplicationContext类。
主要方法有两个
this.readXML(filename);//读取XML配置文件
this.instanceBeans();//实例化bean
主要成员变量
private List<BeanDefinition> beanDefines = new ArrayList<BeanDefinition>();//保存XML中的bean信息,包括id和class,组成BeanDefinition类
private Map<String, Object> sigletons = new HashMap<String, Object>();//保存实例化以后的bean
具体的代码实现不细化研究了,大概流程:
首先使用dom4j读取XML中的bean标签,把id和class保存在beanDifines数组中,然后在instanceBeans()方法中遍历beanDifines数组,取出类名,利用发射技术进行实例化,如Class.forName(beanDefinition.getClassName()).newInstance());并把实例化对象保存在sigletons的哈希表中。完成实例化
接下来就是简单的get方法来取出对应的bean供其他类调用。
可以发现bean的实例化就是在创建ApplicationContext对象的时候,通过构造方法进行实例化的。
代码:
ItcastClassPathApplicationContext.java
package junit.test;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.XPath;
import org.dom4j.io.SAXReader;
public class ItcastClassPathXMLApplicationContext {
private List<BeanDefinition> beanDefines = new ArrayList<BeanDefinition>();
private Map<String, Object> sigletons = new HashMap<String, Object>();
public ItcastClassPathXMLApplicationContext(String filename){
this.readXML(filename);
this.instanceBeans();
}
/**
* 完成bean的实例化
*/
private void instanceBeans() {
for(BeanDefinition beanDefinition : beanDefines){
try {
if(beanDefinition.getClassName()!=null && !"".equals(beanDefinition.getClassName().trim()))
sigletons.put(beanDefinition.getId(), Class.forName(beanDefinition.getClassName()).newInstance());
} catch (Exception e) {
e.printStackTrace();
}
}
}
/**
* 读取xml配置文件
* @param filename
*/
private void readXML(String filename) {
SAXReader saxReader = new SAXReader();
Document document=null;
try{
URL xmlpath = this.getClass().getClassLoader().getResource(filename);
document = saxReader.read(xmlpath);
Map<String,String> nsMap = new HashMap<String,String>();
nsMap.put("ns","http://www.springframework.org/schema/beans");//加入命名空间
XPath xsub = document.createXPath("//ns:beans/ns:bean");//创建beans/bean查询路径
xsub.setNamespaceURIs(nsMap);//设置命名空间
List<Element> beans = xsub.selectNodes(document);//获取文档下所有bean节点
for(Element element: beans){
String id = element.attributeValue("id");//获取id属性值
String clazz = element.attributeValue("class"); //获取class属性值
BeanDefinition beanDefine = new BeanDefinition(id, clazz);
beanDefines.add(beanDefine);
}
}catch(Exception e){
e.printStackTrace();
}
}
/**
* 获取bean实例
* @param beanName
* @return
*/
public Object getBean(String beanName){
return this.sigletons.get(beanName);
}
}
BeanDefinition.java
package junit.test;
public class BeanDefinition {
private String id;
private String className;
public BeanDefinition(String id, String className) {
this.id = id;
this.className = className;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getClassName() {
return className;
}
public void setClassName(String className) {
this.className = className;
}
}
测试代码
ItcastClassPathXMLApplicationContext ctx = new ItcastClassPathXMLApplicationContext("beans.xml");
PersonService personService = (PersonService)ctx.getBean("personService");
personService.save();
1.1.4 三种实例化bean的方式
1.使用类构造器实例化
<bean id="personService" class="service.impl.PersonServiceBean"></bean>
2.使用静态工厂方法实例化
beans.xml
<bean id="personService2" class="service.impl.PersonServiceBeanFactory" factory-method="createPersonServiceBean"></bean>
PersonServiceBeanFactory.java
1. public static PersonServiceBean createPersonServiceBean(){
2. return new PersonServiceBean();
3. }
3.使用实例工厂方法实例化
beans.xml
<bean id="personServiceFactory" class="service.impl.PersonServiceBeanFactory"></bean>
<bean id="personService3" factory-bean="personServiceFactory" factory-method="createPersonServiceBean2"></bean>
PersonServiceBeanFactory.java
1. public PersonServiceBean createPersonServiceBean2(){
2. return new PersonServiceBean();
3. }
三种方式测试成功
绝大部分都使用第一种实例化方法
1.1.5 spring管理bean的作用域
来看一段代码
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
PersonService personService1 = (PersonService)context.getBean("personService");
PersonService personService2 = (PersonService)context.getBean("personService");
System.out.println(personService1==personService2);
输出结果为true,说明getBean方法获取的是单实例
那么就来看bean的作用域
bean标签的scope属性来设置
singleton
默认情况下的单例模式,每次调用getBean方法获取的都是同一个bean对象
默认情况喜爱会在容器启动时初始化bean,但我们可以指定Bean节点的lazy-init属性来延迟初始化bean,这时候,只有第一次获取bean才会初始化bean。
<bean id="xxx" class="xxx" lazy_init="true"/>
如果对所有bean都应用延迟初始化
<beans default-lazy-init="true"...>
prototype
每次从容器获取bean都是一个新的对象
request
session
global session
1.1.6 spring管理bean的生命周期
从前面可以看到,
singleton模式下bean的实例化的时机是在ApplicationContext实例化的时候进行实例化,然而设置lazy-init=true的情况下在getBean方法调用的时候进行实例化。
prototype模式下bean的实例化时机是在getBean方法调用的时候进行实例化。
如果我们需要在bean初始化的时候,打开数据库资源等连接,那么指定一个初始化方法,这样在bean实例化的时候就会自动初始化。同样的指定一个销毁方法。
beans.xml
<bean id="personService" class="service.impl.PersonServiceBean" init-method="init" destroy-method="destroy"></bean>
PersonServiceBean.java
public void init(){
System.out.println("inti初始化方法");
}
public void destroy(){
System.out.println("destroy销毁方法");
}
SprintTest.java
1.AbstractApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
- context.close();
测试结果输出
inti初始化方法destroy销毁方法
lazy-init应该是一个优化spring很好的东西,就像hibernate里的懒加载,有些bean可能一直没有用到过,根本没必要初始化,但是视频里说尽量使用lazy-init=false,为了发现所有bean可能出现的错误,难道测试的时候要这么做?另外JUnit的测试也不是很明白。。。感觉和一般的差不多啊
1.1.7 剖析Spring依赖注入的原理
通过set方法注入
package dao.impl;
import dao.PersonDao;
public class PersonDaoBean implements PersonDao {
/* (non-Javadoc)
* @see dao.impl.PersonDao#add()
*/
public void add(){
System.out.println(this.getClass().getName()+" add方法");
}
}
PersonServiceBean.java
• package service.impl;
•
• import dao.PersonDao;
• import service.PersonService;
•
•
• public class PersonServiceBean implements PersonService {
• private PersonDao personDao;
•
• public PersonDao getPersonDao() {
• return personDao;
• }
•
• public void setPersonDao(PersonDao personDao) {
• this.personDao = personDao;
• }
•
• public void save(){
• personDao.add();
• }
•
• public void init(){
• System.out.println("inti初始化方法");
• }
•
• public void destroy(){
• System.out.println("destroy销毁方法");
• }
• }
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"
• xsi:schemaLocation="http://www.springframework.org/schema/beans
• http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
•
• <bean id="personDao" class="dao.impl.PersonDaoBean"></bean>
• <bean id="personService" class="service.impl.PersonServiceBean" lazy-init="false" init-method="init" destroy-method="destroy">
• <property name="personDao" ref="personDao"></property>
• </bean>
•
• </beans>
SpringTest.java
• package junit.test;
•
• import org.junit.Test;
• import org.springframework.context.support.AbstractApplicationContext;
• import org.springframework.context.support.ClassPathXmlApplicationContext;
•
• import service.PersonService;
•
• public class SpringTest {
• @Test
• public void instanceSpring(){
• AbstractApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
• PersonService personService = (PersonService)context.getBean("personService");
• personService.save();
• context.close();
•
• }
•
• }
运行结果:
dao.impl.PersonDaoBean add方法
还有一种内部bean的注入方式
·<bean id="personService" class="service.impl.PersonServiceBean" lazy-init="false" init-method="init" destroy-method="destroy">
· <property name="personDao">
· <bean class="dao.impl.PersonDaoBean"></bean>
· </property>
· </bean>
即在property标签内部添加bean标签,在bean标签里写出类名即可。效果一样。
实际上spring的操作流程是吧bean中的property也保存在一个数组中,初始化的时候遍历数组,找到需要注入的bean,利用反射技术调用set方法,参数为配置文件中的ref应用的bean,就完成了注入。
1.1.8 Spring装配基本属性的原理
上一篇的注入都是对象的注入,这篇来分析基本属性的注入。
以String类型为例
PersonServiceBean.java中加入
private String name;
beans.xml
·<bean id="personService" class="service.impl.PersonServiceBean" lazy-init="false" init-method="init" destroy-method="destroy">
· <property name="personDao">
· <bean class="dao.impl.PersonDaoBean"></bean>
· </property>
· <property name="name" value="pf"></property>
· </bean>
其他基本类型类似,均采用value属性方式赋值。
1.1.9 Spring如何装配各种集合类型的属性
set:
private Set<String> set;
· <property name="set">
· <set>
· <value>第一个</value>
· <value>第二个</value>
· <value>第三个</value>
· </set>
· </property>
list:
private List<String> list;
· <property name="list">
· <list>
· <value>第一个</value>
· <value>第二个</value>
· <value>第三个</value>
· </list>
· </property>
properties:
private Properties properties;
· <property name="properties">
· <props>
· <prop key="key1">value1</prop>
· <prop key="key2">value2</prop>
· <prop key="key3">value3</prop>
· </props>
· </property>
map:
peivate Map<String,String> map;
· <property name="map">
· <map>
· <entry key="key1" value="value1"></entry>
· <entry key="key2" value="value2"></entry>
· <entry key="key3" value="value3"></entry>
· </map>
· </property>
1.1.10 使用构造器装配属性
PersonServiceBean.java
• package service.impl;
•
• import java.util.Set;
•
• import dao.PersonDao;
• import service.PersonService;
•
•
• public class PersonServiceBean implements PersonService {
• private PersonDao personDao;
• private String name;
•
• public PersonServiceBean(PersonDao personDao, String name) {
• this.personDao = personDao;
• this.name = name;
• }
• @Override
• public String getName() {
• // TODO Auto-generated method stub
• return this.name;
• }
• @Override
• public PersonDao getPersonDao() {
• // TODO Auto-generated method stub
• return this.personDao;
• }
• @Override
• public void save() {
• // TODO Auto-generated method stub
• System.out.println(this.getClass().getName()+" save方法");
• }
• public void setPersonDao(PersonDao personDao) {
• this.personDao = personDao;
• }
• public void setName(String name) {
• this.name = name;
• }
•
• }
beans.xml
• <bean id="personService" class="service.impl.PersonServiceBean">
•
• <constructor-arg index="0" ref="personDao"></constructor-arg>
• <constructor-arg index="1" value="pf"></constructor-arg>
•
• </bean>
index指明了参数的位置,加了index就不用加type来说明了。
1.1.11 用@Resource注解完成属性装配
java代码注入配置,需要spring解压文件夹下lib/j2ee/common-annotation.jar这个库文件,添加玩以后,修改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"
• xsi:schemaLocation="http://www.springframework.org/schema/beans
• http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
• http://www.springframework.org/schema/context
• http://www.springframework.org/schema/context/spring-context-2.5.xsd">
•
• <context:annotation-config/>
• </beans>
由于现在家里上spring的网站总是上不去,如果要出现标签提示,那么和前面一样在本地添加spring-context-2.5.xsd
现在配置工作完成了,现在开始用java代码来完成注入
@Autowired方式:默认按类型装配,默认情况下它要求以来对象必须存在,如果允许null值,可以设置它required属性为false。如果我们想使用按名称装配,可以结合@Qualifier注解一起使用
@Autowired @Qualifier("personDaoBean")
private PersonDao personDao;
@Resource方式:默认按名称装配,名称可以通过@Resource的name属性指定,如果没有指定的name属性,当注解标注在字段上,即默认取字段的名称作为bean名称寻找以来对象,当注解标注在属性的setter方法上,即迷人取属性名作为bean名称寻找以来对象。
@Resource(name="personDaoBean")
private PersonDao personDao;
推荐使用@Resource方式,因为@Resource是j2ee里的一个注解,而@AutoWired是spring里的注解,使用@Resource可以降低与框架的耦合度。
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"
• xsi:schemaLocation="http://www.springframework.org/schema/beans
• http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
• http://www.springframework.org/schema/context
• http://www.springframework.org/schema/context/spring-context-2.5.xsd">
•
• <context:annotation-config/>
•
• <bean id="personDao" class="dao.impl.PersonDaoBean"/>
• <bean id="personService" class="service.impl.PersonServiceBean"/>
•
• </beans>
PersonServiceBean.java
• package service.impl;
•
• import javax.annotation.Resource;
•
• import dao.PersonDao;
• import service.PersonService;
•
•
• public class PersonServiceBean implements PersonService {
• @Resource private PersonDao personDao;
• private String name;
•
• public PersonServiceBean() {
•
• }
• @Override
• public String getName() {
• // TODO Auto-generated method stub
• return this.name;
• }
• @Override
• public PersonDao getPersonDao() {
• // TODO Auto-generated method stub
• return this.personDao;
• }
• @Override
• public void save() {
• // TODO Auto-generated method stub
• System.out.println(this.getClass().getName()+" save方法");
• }
• public void setPersonDao(PersonDao personDao) {
• this.personDao = personDao;
• }
• public void setName(String name) {
• this.name = name;
• }
• }
运行发现注入成功,string类型就不需要用注解注入了,直接赋值就可以了。
另外吧@Resource放在setter方法上也是可以的,效果一样。
1.1.12 编码剖析@Resource注解的实现原理
ItcastResource.java
• package junit.test;
•
• import java.lang.annotation.ElementType;
• import java.lang.annotation.Retention;
• import java.lang.annotation.RetentionPolicy;
• import java.lang.annotation.Target;
•
• @Retention(RetentionPolicy.RUNTIME)
• @Target({ElementType.FIELD, ElementType.METHOD})
• public @interface ItcastResource {
• public String name() default "";
• }
PropertyDefinition .java
• package junit.test;
•
• public class PropertyDefinition {
• private String name;
• private String ref;
• private String value;
•
• public String getValue() {
• return value;
• }
•
• public void setValue(String value) {
• this.value = value;
• }
•
• public PropertyDefinition(String name, String ref, String value) {
• this.name = name;
• this.ref = ref;
• this.value = value;
• }
•
• public String getName() {
• return name;
• }
• public void setName(String name) {
• this.name = name;
• }
• public String getRef() {
• return ref;
• }
• public void setRef(String ref) {
• this.ref = ref;
• }
•
• }
BeanDefinition.java
• package junit.test;
•
• import java.util.ArrayList;
• import java.util.List;
•
• public class BeanDefinition {
• private String id;
• private String className;
• private List<PropertyDefinition> propertys = new ArrayList<PropertyDefinition>();
•
• public BeanDefinition(String id, String className) {
• this.id = id;
• this.className = className;
• }
• public String getId() {
• return id;
• }
• public void setId(String id) {
• this.id = id;
• }
• public String getClassName() {
• return className;
• }
• public void setClassName(String className) {
• this.className = className;
• }
• public List<PropertyDefinition> getPropertys() {
• return propertys;
• }
• public void setPropertys(List<PropertyDefinition> propertys) {
• this.propertys = propertys;
• }
• }
ItcastClassPathXMLApplicationContext.java
• package junit.test;
•
• import java.beans.Introspector;
• import java.beans.PropertyDescriptor;
• import java.lang.reflect.Field;
• import java.lang.reflect.Method;
• import java.net.URL;
• import java.util.ArrayList;
• import java.util.HashMap;
• import java.util.List;
• import java.util.Map;
•
• import org.apache.commons.beanutils.ConvertUtils;
• import org.dom4j.Document;
• import org.dom4j.Element;
• import org.dom4j.XPath;
• import org.dom4j.io.SAXReader;
• public class ItcastClassPathXMLApplicationContext {
• private List<BeanDefinition> beanDefines = new ArrayList<BeanDefinition>();
• private Map<String, Object> sigletons = new HashMap<String, Object>();
•
• public ItcastClassPathXMLApplicationContext(String filename){
• this.readXML(filename);
• this.instanceBeans();
• this.annotationInject();
• this.injectObject();
• }
• /**
• * 通过注解实现注入依赖对象
• */
• private void annotationInject() {
• for(String beanName : sigletons.keySet()){
• Object bean = sigletons.get(beanName);
• if(bean!=null){
• try {
• PropertyDescriptor[] ps = Introspector.getBeanInfo(bean.getClass()).getPropertyDescriptors();
• for(PropertyDescriptor properdesc : ps){
• Method setter = properdesc.getWriteMethod();//获取属性的setter方法
• if(setter!=null && setter.isAnnotationPresent(ItcastResource.class)){
• ItcastResource resource = setter.getAnnotation(ItcastResource.class);
• Object value = null;
• if(resource.name()!=null && !"".equals(resource.name())){
• value = sigletons.get(resource.name());
• }else{
• value = sigletons.get(properdesc.getName());
• if(value==null){
• for(String key : sigletons.keySet()){
• if(properdesc.getPropertyType().isAssignableFrom(sigletons.get(key).getClass())){
• value = sigletons.get(key);
• break;
• }
• }
• }
• }
• setter.setAccessible(true);
• setter.invoke(bean, value);//把引用对象注入到属性
• }
• }
• Field[] fields = bean.getClass().getDeclaredFields();
• for(Field field : fields){
• if(field.isAnnotationPresent(ItcastResource.class)){
• ItcastResource resource = field.getAnnotation(ItcastResource.class);
• Object value = null;
• if(resource.name()!=null && !"".equals(resource.name())){
• value = sigletons.get(resource.name());
• }else{
• value = sigletons.get(field.getName());
• if(value==null){
• for(String key : sigletons.keySet()){
• if(field.getType().isAssignableFrom(sigletons.get(key).getClass())){
• value = sigletons.get(key);
• break;
• }
• }
• }
• }
• field.setAccessible(true);//允许访问private字段
• field.set(bean, value);
• }
• }
• } catch (Exception e) {
• e.printStackTrace();
• }
• }
• }
• }
•
• /**
• * 为bean对象的属性注入值
• */
• private void injectObject() {
• for(BeanDefinition beanDefinition : beanDefines){
• Object bean = sigletons.get(beanDefinition.getId());
• if(bean!=null){
• try {
• PropertyDescriptor[] ps = Introspector.getBeanInfo(bean.getClass()).getPropertyDescriptors();
• for(PropertyDefinition propertyDefinition : beanDefinition.getPropertys()){
• for(PropertyDescriptor properdesc : ps){
• if(propertyDefinition.getName().equals(properdesc.getName())){
• Method setter = properdesc.getWriteMethod();//获取属性的setter方法 ,private
• if(setter!=null){
• Object value = null;
• if(propertyDefinition.getRef()!=null && !"".equals(propertyDefinition.getRef().trim())){
• value = sigletons.get(propertyDefinition.getRef());
• }else{
• value = ConvertUtils.convert(propertyDefinition.getValue(), properdesc.getPropertyType());
• }
• setter.setAccessible(true);
• setter.invoke(bean, value);//把引用对象注入到属性
• }
• break;
• }
• }
• }
• } catch (Exception e) {
• }
• }
• }
• }
• /**
• * 完成bean的实例化
• */
• private void instanceBeans() {
• for(BeanDefinition beanDefinition : beanDefines){
• try {
• if(beanDefinition.getClassName()!=null && !"".equals(beanDefinition.getClassName().trim()))
• sigletons.put(beanDefinition.getId(), Class.forName(beanDefinition.getClassName()).newInstance());
• } catch (Exception e) {
• e.printStackTrace();
• }
• }
•
• }
• /**
• * 读取xml配置文件
• * @param filename
• */
• private void readXML(String filename) {
• SAXReader saxReader = new SAXReader();
• Document document=null;
• try{
• URL xmlpath = this.getClass().getClassLoader().getResource(filename);
• document = saxReader.read(xmlpath);
• Map<String,String> nsMap = new HashMap<String,String>();
• nsMap.put("ns","http://www.springframework.org/schema/beans");//加入命名空间
• XPath xsub = document.createXPath("//ns:beans/ns:bean");//创建beans/bean查询路径
• xsub.setNamespaceURIs(nsMap);//设置命名空间
• List<Element> beans = xsub.selectNodes(document);//获取文档下所有bean节点
• for(Element element: beans){
• String id = element.attributeValue("id");//获取id属性值
• String clazz = element.attributeValue("class"); //获取class属性值
• BeanDefinition beanDefine = new BeanDefinition(id, clazz);
• XPath propertysub = element.createXPath("ns:property");
• propertysub.setNamespaceURIs(nsMap);//设置命名空间
• List<Element> propertys = propertysub.selectNodes(element);
• for(Element property : propertys){
• String propertyName = property.attributeValue("name");
• String propertyref = property.attributeValue("ref");
• String propertyValue = property.attributeValue("value");
• PropertyDefinition propertyDefinition = new PropertyDefinition(propertyName, propertyref, propertyValue);
• beanDefine.getPropertys().add(propertyDefinition);
• }
• beanDefines.add(beanDefine);
• }
• }catch(Exception e){
• e.printStackTrace();
• }
• }
• /**
• * 获取bean实例
• * @param beanName
• * @return
• */
• public Object getBean(String beanName){
• return this.sigletons.get(beanName);
• }
• }
实际上也就是通过了反射技术来构造对象并且赋值,只是用到了注解的方法,并且利用@interface来构造自定义的注解类型。
1.1.13 @Autowire注解与自动装配
使用了@Autowired的注解方式,这种默认按类型查找符合的bean注入
@Autowired **private** PersonDao personDao;
使用@Qualifier注明bean名称注入
@Autowired @Qualifier("personDao") **private** PersonDao personDao;
还可以添加required属性,在没找到bean的情况下,如果required为false,则注入null,required为true,则报错。
@Autowired(required=true) @Qualifier("personDao") private PersonDao personDao;
自动装配:
通过bean标签的autowire属性来配置,有5种值
no 不使用自动装配,必须通过ref元素指定依赖,默认设置。
byName 根据属性名自动装配。此选项将检查容器并根据名字查找与
属性完全一致的bean,并将其与属性自动装配。
byType 如果容器中存在一个与指定属性类型相同的bean,那么将与
该属性自动装配;如果存在多个该类型bean,那么抛出异
常,并指出不能使用byType方式进行自动装配;如果没有找
到相匹配的bean,则什么事都不发生,也可以通过设置
dependency-check="objects"让Spring抛出异常。
constructor 与byType方式类似,不同之处在于它应用于构造器参数。如
果容器中没有找到与构造器参数类型一致的bean,那么抛出
异常。
autodetect 通过bean类的自省机制(introspection)来决定是使用
constructor还是byType方式进行自动装配。如果发现默认的
构造器,那么将使用byType方式。
1.1.14 让Spring自动扫描和管理Bean
让Spring自动扫描和管理Bean
<context:component-scan base-package="cn.test"></context:component-scan>
其中base-package为需要扫描的包(含子包)
@Service用于标注业务层组件,@Controller用于标注控制层组件(如struts中的action),@Repository用于标注数据访问组件,即DAO组件,而@Component泛指组件,当组件不好归类的时候,我们可以使用这个注解进行标注。
bean的默认名称是类名,然后把第一个字母改为小写。可以通过@Service("xxx")修改bean名称。
这种bean默认是单例的,如果想改变,可以使用@Service(“aaaaa”) @Scope(“prototype”)来改变。
还可以通过@PostConstruct @PreDestroy设置初始化和销毁的函数
• @PostConstruct
• public void init(){
• System.out.println("初始化");
• }
•
•
•
• @PreDestroy
• public void destory(){
• System.out.println("开闭资源");
• }
1.1.15 使用JDK中的Proxy技术实现AOP功能
通过代理对象来调用对象的方法,从而做出权限控制。
目标对象必须实现接口才能使用proxy技术创建代理对象。
PersonService.java
• package cn.pf.aop.service;
•
• public interface PersonService {
• public void save(String name);
• public void update(String name, Integer personId);
• public String getName(Integer personId);
• }
PersonServiceBean.java
• package cn.pf.aop.service.impl;
•
• import cn.pf.aop.service.PersonService;
•
• public class PersonServiceBean implements PersonService {
• private String user = null;
•
• public PersonServiceBean() {
•
• }
•
• public PersonServiceBean(String user) {
• this.setUser(user);
• }
•
• @Override
• public String getName(Integer personId) {
• System.out.println(this.getClass().getName()+" getName方法");
• return "pf";
• }
•
• @Override
• public void save(String name) {
• System.out.println(this.getClass().getName()+" save方法");
• }
•
• @Override
• public void update(String name, Integer personId) {
• System.out.println(this.getClass().getName()+" update方法");
• }
•
• public void setUser(String user) {
• this.user = user;
• }
•
• public String getUser() {
• return user;
• }
•
• }
JDKProxyFactory.java
• package cn.pf.aop;
•
• import java.lang.reflect.InvocationHandler;
• import java.lang.reflect.Method;
• import java.lang.reflect.Proxy;
•
• import cn.pf.aop.service.impl.PersonServiceBean;
•
• public class JDKProxyFactory implements InvocationHandler {
• private Object targetObject;
•
• public Object createProxyIntance(Object targetObject){
• this.targetObject = targetObject;
• return Proxy.newProxyInstance(this.targetObject.getClass().getClassLoader(),
• this.targetObject.getClass().getInterfaces(), this);
• }
•
• @Override
• public Object invoke(Object proxy, Method method, Object[] arg2)
• throws Throwable {
• PersonServiceBean personServiceBean = (PersonServiceBean)targetObject;
• Object result = null;
• if(personServiceBean.getUser() != null){
• result = method.invoke(targetObject, arg2);
• }
• return null;
• }
• }
AOPTest.java
• package junit.test;
•
• import org.junit.Test;
•
• import cn.pf.aop.JDKProxyFactory;
• import cn.pf.aop.service.PersonService;
• import cn.pf.aop.service.impl.PersonServiceBean;
•
•
• public class AOPTest {
• @Test public void proxyTest(){
• JDKProxyFactory factory = new JDKProxyFactory();
• PersonService personService = (PersonService) factory.createProxyIntance(new PersonServiceBean());
• personService.save("111");
• }
• }
Proxy.newProxyInstance(this.targetObject.getClass().getClassLoader(), this.targetObject.getClass().getInterfaces(), this);
创建代理对象的时候,加入了该目标对象所有的接口,即对所有的方法进行监听,任何一个方法的调用都会触发代理对象的invoke方法。this表示触发哪个代理对象的invoke方法,这里我们设置当前代理对象。
调用personService的save方法的时候,可以理解为,save方法被监听,进入代理对象的invoke方法,如果user!=null,则invoke方法中调用了personService的save方法,如果user==null,则什么也不做。
通过反射技术调用方法其实可以简单的理解为
xxx.invoke(obj,args)返回的结果是obj.xxx(args)
1.1.16 使用CGLIB实现AOP功能与AOP概念解释
前面的proxy技术必须在类实现了接口的前提下才可以实现权限的控制,cglb可以在类不实现接口的情况下完成。
在spring文件夹下lib/cglib下找到cglib的jar库文件,加入工程。
CGlibProxyFactory.java
• package cn.pf.aop;
•
• import java.lang.reflect.Method;
•
• import cn.pf.aop.service.impl.PersonServiceBean;
•
• import net.sf.cglib.proxy.Enhancer;
• import net.sf.cglib.proxy.MethodInterceptor;
• import net.sf.cglib.proxy.MethodProxy;
•
• public class CGlibProxyFactory implements MethodInterceptor{
• private Object targetObject;
•
• public Object createProxyIntance(Object targetObject){
• this.targetObject = targetObject;
• Enhancer enhancer = new Enhancer();
• enhancer.setSuperclass(this.targetObject.getClass());
• enhancer.setCallback(this);
• return enhancer.create();
• }
•
• @Override
• public Object intercept(Object proxy, Method method, Object[] arg2,
• MethodProxy arg3) throws Throwable {
• PersonServiceBean personServiceBean = (PersonServiceBean)targetObject;
• Object result = null;
• if(personServiceBean.getUser() != null){
• result = method.invoke(targetObject, arg2);
• }
• return result;
• }
• }
AOPTest.java
• @Test public void proxyTest2(){
• CGlibProxyFactory factory = new CGlibProxyFactory();
• PersonServiceBean personServiceBean = (PersonServiceBean) factory.createProxyIntance(new PersonServiceBean("1"));
• personServiceBean.save("111");
• }
CGlib的enhance继承了目标类所有非final方法,对这些方法进行覆盖。创建的代理对象是目标对象的子类
1.1.17 使用Spring的注解方式实现AOP入门
首先添加包
``
/spring.jar
/lib/aspectj/aspectjrt.jar
/lib/aspectj/aspectjweaver.jar
/lib/j2ee/common-annotations.jar
/lib/jakarta-commons/common_logging.jar
/lib/cglib/cglib-nodep-2.1-3.jar
``
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:aop="http://www.springframework.org/schema/aop"
• xsi:schemaLocation="http://www.springframework.org/schema/beans
• http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
• http://www.springframework.org/schema/aop
• http://www.springframework.org/schema/context/spring-aop-2.5.xsd">
•
• <aop:aspectj-autoproxy/>
• </beans>
PersonService.java和PersonServiceBean.java和上篇一样
MyInterceptor.java
@Ascept声明了切面,即进行拦截的类。
@Pointcut声明了切入点,即进行拦截的方法。
@Pointcut("execution(* cn.itcast.service...(..))")
- 代表返回值类型
cn.pf.service 需要拦截的包名
.. 代表队子包的类进行拦截
代表进行拦截的类
代表进行拦截的方法
(..) 代表方法的参数随意
(*代表任意)
下面来测试前置通知,后置通知,最终通知,例外通知以及环绕通知。
MyInterceptor.java
• package cn.pf.aop.service;
•
• import org.aspectj.lang.ProceedingJoinPoint;
• import org.aspectj.lang.annotation.After;
• import org.aspectj.lang.annotation.AfterReturning;
• import org.aspectj.lang.annotation.AfterThrowing;
• import org.aspectj.lang.annotation.Around;
• import org.aspectj.lang.annotation.Aspect;
• import org.aspectj.lang.annotation.Before;
• import org.aspectj.lang.annotation.Pointcut;
•
• @Aspect
• public class MyInterceptor {
• @Pointcut("execution(* cn.pf.aop.service.impl.PersonServiceBean.*(..))")
• private void anyMethod(){}
•
• @Before("anyMethod()")
• public void doAccessCheck(){
• System.out.println("前置通知");
• }
•
• @AfterReturning("anyMethod()")
• public void doAfterReturning(){
• System.out.println("后置通知");
• }
•
• @After("anyMethod()")
• public void doAfter(){
• System.out.println("最终通知");
• }
•
• @AfterThrowing("anyMethod()")
• public void doAfterThrowing(){
• System.out.println("例外通知");
• }
•
• @Around("anyMethod()")
• public Object doBasicProfiling(ProceedingJoinPoint pjp)throws Throwable{
• System.out.println("进入环绕方法");
• Object result = pjp.proceed();
• System.out.println("退出环绕方法");
• return result;
• }
• }
SpringAOPTest.java
• import org.junit.Test;
• import org.springframework.context.ApplicationContext;
• import org.springframework.context.support.ClassPathXmlApplicationContext;
•
• import cn.pf.aop.service.PersonService;
•
•
•
• public class SpringAOPTest {
• @Test public void interceptorTest(){
• ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
• PersonService personService = (PersonService)context.getBean("personService");
• personService.save("1");
• }
• }
控制台输出:
前置通知
进入环绕方法
cn.pf.aop.service.impl.PersonServiceBean save方法
后置通知
最终通知
退出环绕方法
那么如何获得输入参数,返回值,异常呢,那么稍作修改
• @Before("anyMethod() && args(name)")
• public void doAccessCheck(String name) {
• System.out.println("前置通知:"+ name);
• }
• @AfterReturning(pointcut="anyMethod()",returning="result")
• public void doAfterReturning(String result) {
• System.out.println("后置通知:"+ result);
• }
• @After("anyMethod()")
• public void doAfter() {
• System.out.println("最终通知");
• }
• @AfterThrowing(pointcut="anyMethod()",throwing="e")
• public void doAfterThrowing(Exception e) {
• System.out.println("例外通知:"+ e);
• }
其实切面就感觉像servlet里面的过滤器,在方法的前后加上一些关卡,进行筛选,判定权限,通过指定好的一些切面后,才可以真正调用目标对象的方法。
1.1.18 基于XML配置方式声明切面
基于XML配置方式声明切面
与注释方法没什么太大的区别
• <bean id=”orderservice” class=”cn.service.OrderServiceBean” />
•
• <bean id=”log” class=”cn.service.LogPrint” />
•
• <aop:config>
•
• <aop:aspect id=”myaop” ref=”log”>
•
• <aop:pointcut id=”mycut” expression=”execution(* cn.service..*.*(..))”/>
•
• <aop:before pointcut-ref=”mycut” method=”doAccessCheck” />
•
• <aop:after-returning pointcut-ref=”mycut” method=”doReturnCheck” />
•
• <aop:after-throwing pointcut-ref=”mycut” method=”doExceptionAction” />
•
• <aop:after pointcut-ref=”mycut” method=”doReleaseAction” />
•
• <aop:around pointcut-ref=”mycut” method=”doBasicProfiling” />
•
• </aop:aspect>
•
• </aop:config>
1.1.19 aspectj的切入点语法定义细节
execution(* cn.pf.aop.service.impl.PersonServiceBean.*(..))
所有非final方法
execution(!void cn.pf.aop.service.impl.PersonServiceBean.*(..))
非void非final方法
execution(java.lang.String cn.pf.aop.service.impl.PersonServiceBean.*(..))
非final且返回类型为String的方法
execution(java.lang.String cn.pf.aop.service.impl.PersonServiceBean.*(java.lang.String,..))
第一个参数为String的非final方法
execution(* cn.pf.aop.service.impl..*.*(..))
对包下所有类进行拦截