Spring知识点笔记

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

11997621-0c40c0373c6451ba.png
image.png

(不要勾选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");  
  1. 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..*.*(..))对包下所有类进行拦截


11997621-d941d4a1f9683030.png
image.png
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值