1.   装配各种类型的属性

1.1简单属性

使用<bean>的value属性或<property>子标签<value>装配。
<propertyname="name"value="张无忌"></property>
<!-- property元素中的内容叫属性值,会自动把value描述的值转换成对应属性的类型 -->
<propertyname="age"><value>68</value></property>
<!--  也可以使用value来装配一些Spring支持的类型URL,Class -->
<propertyname="homePage"value="http://www.baidu.cn"></property>

1.2引用其它bean

使用ref属性或标签。

<propertyname="parent">
<!-- ref引用其它的 bean,local表示引用本容器中的bean,parent表示引用父容器中某个bean,bean表示引入某个bean,先在当容器中找,然后再到父容器中找 -->
<refbean="p0"/></property>

1.3内部bean

直接使用<bean>定义一个新的bean,一般不用指定该bean的名称。

<propertyname="parent">
<!--  值是内部Bean ,一般不需要指定bean的名称 -->
<beanclass="cn.itcasg.gz.springioc.Person">
<constructor-argvalue="张三丰"></constructor-arg>
</bean>
</property>

1.4装配集合

1)数组

<!-- 装配数组 -->
          <propertyname="favs">
                <array><!--也可以使用<list>标签 -->
                     <value>足球</value>
                     <value>蓝球</value>
                     <value>音乐</value>
                </array>
           </property>

2)List

  

<!-- 装配list -->
          <propertyname="schools">
                <list><!--也可以使用<array>标签 -->
                     <value>北大</value>
                     <value>清华</value>
                </list>
           </property>

3)Set    

<!-- 装配set -->
          <propertyname="citys">
                <set>
                     <value>广州</value>
                     <value>北京</value>
                     <value>西安</value>
                     <value>广州</value><!--最终方set中的只有三个元素-->
                </set>
           </property>

4)Map

 

<!-- 装配Map -->
          <propertyname="scores">
                <map>
                     <entry key="语文"value="50"/>
                     <entry key="数学"value="30"/>
                     <entry key="历史"value="20"/>
                     <!-- key-ref,value-ref属性用来引用其它bean -->
                     <entry key-ref="p1"value-ref="p2"/>
                </map>
           </property>

5)Properties

      

<!-- 装配属性类型 -->
          <propertyname="ims">
          <props>
          <prop key="qq">4858458</prop>
          <prop key="msn">caishiyou@sina.com</prop>
          </props>
          </property>
          <!-- 在value中直接使用键值对来作为属性内容 -->
          <propertyname="ims2">
          <value>
                     qq=58568565868
                     msn=kdkdkf@ddd.com
                </value>
             </property>

1.5装配空值null  

<bean  id="p2"class="cn.itcasg.gz.springioc.Person">
          <propertyname="name"value="小链子"/>
          <propertyname="age">
                <!-- null标签用来指定空值 -->
                <null/>
          </property>
      </bean>

1.6装配复杂类型属性

publicclass Person {
       private Stringname;
       private Integerage;
       private Personparent; //父亲,复杂类型
       private Datebirthday; //生日,复杂类型
}

1) 使用value来注入复杂类型的对象

如果使用value来注入复杂类型的对象,要依赖的对象提供带一个参数(并且类型为String)的构造函数,Spring就可以将其转换成需要的类型。

<bean  id="p1"class="com.maple.spring.customtype.Person">
          <propertyname="name"value="张三"/>
          <propertyname="age"value="23"/>
          <!-- parent为Person类型,如果使用value来注入复杂类型的对象只要
依赖的对象提供带一个参数(并且类型为String)的构造函数,Spring就可以将其转换成需要的类型。 -->
          <propertyname="parent"value="李四/>
      </bean>

2)自定义类型编辑器

使用value来注入复杂类型对象时,是不完整的。这时就要自定义类型编辑器,自定义类型编辑器时继承PropertyEditorSupport类,并重写其setAsText方法。

publicclass MyDateEditorextendsPropertyEditorSupport {
//    PropertyEditorSupport的子类中有日期编辑器
       private SimpleDateFormatsdf = new SimpleDateFormat("yyyy-MM-dd");
       @Override
       publicvoid setAsText(Stringtext)throws IllegalArgumentException {
          System.out.println("执行编辑器的setAsText方法....");
          if(text !=null &&text.trim().length() > 0) {
                try {
                     Datedate = sdf.parse(text);//转换
                     this.setValue(date);//将转换后的值设置进去
                }catch (ParseException e) {
                     e.printStackTrace();
                     super.setAsText(text);
                }
          }     
      
}

3)注册自定义编辑器

 

<!-- 注册类型编辑器,从BeanFactoryPostProcessor接口的实现类 -->
<beanclass="org.springframework.beans.factory.config.CustomEditorConfigurer">
          <!--customEditors是该类的一个Map类型属性 -->
          <propertyname="customEditors">
                <map>
<!-- key:要转换的类型 value:指定使用哪个转换器来转换 -->
<entrykey="java.util.Date"value="com.maple.spring.customtype.MyDateEditor"/>
                </map>
          </property>
      </bean>

2.注解装配解决方案1(XML+注解配合)

用法:xml用来定义Bean的信息;注解用来配置依赖信息。

使用到的标签:

@Resource(Java标签的资源注解)(推荐使用)
@Autowired(Spring自定的注解),注解可以加在setter方法上(setter注入)/可以加在Field上(Field注入)。
@Autowired--按类型注入。required=false用来指定该依赖的对象是否是必须的。(false非必须,true必须的,默认true)。
@Qualifier("personDao2")--用来指定要注入的Bean叫什么名字,与@Autowired配合使用。
       @Autowired
       @Qualifier("personDao2")//要注入的Bean的名称为personDao2,会到IoC容器中找
       publicvoid setDao(IPersonDaodao) {
          this.dao = dao;
      }
@Resource--按名字注入,找到名字相同,则直接注入。找不到名字相同,则找类型。可在使用name属性来指定要注入的Bean的名称如:@Resource(name=”personDao”)。
       //要注入的Bean的名称为personDao,会到IoC容器中找
       @Resource(name="personDao")
       publicvoid setDao(IPersonDaodao) {
          this.dao = dao;
      }
 
beans.xml:
<?xmlversion="1.0"encoding="UTF-8"?>
<beansxmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
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-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
       <!-- 开启注解配置,必能省,注意加入context命名空间 -->
     <context:annotation-config/>
       <bean  id="personDao"class="cn.itcasg.gz.springioc.PersonDaoImpl"/>
       <bean  id="personDao2"class="cn.itcasg.gz.springioc.PersonDaoImpl"/>
       <bean  id="dao"class="cn.itcasg.gz.springioc.Person"/>
       <bean  id="personService"class="cn.itcasg.gz.springioc.PersonServiceImpl"/>
</beans>

3.注解装配解决方案2(完全使用注解)

1)指定到IoC容器中的业务组件

实现类上添加注解:

@Repository(持久层组件)
@Service(业务层的组件)
@Controller(控件层组件)
@Component(不属于以上三层的组件)

放到容器中的Bean的名字默认是类名首字母变小写,使用value属性就可以指定具体的Bean名称。如:@Service("personService")bean的名字为personService。

 Bean的作用域:默认是单例singleton。其它作用域,则配合使用@Scope标签来指定。

如:@Scope("prototype")
@Service("personService")//存在在容器中bean的名字为personService
@Scope("prototype")//Bean的作用域为propotype,默认是singleton
publicclass PersonServiceImpl2implements IPersonService {
....//省略
}

2)给组件指定依赖

使用@Autowire@Resource标签来指定。

//要注入的Bean的名称为personDao,会到IoC容器中找
       @Resource(name="personDao")
       publicvoid setDao(IPersonDaodao) {
          this.dao = dao;

3)启动Spring的注解扫描功能

在配置文件中使用<context:component-scan>指定让Spring去扫描哪些包,从而找到要放到容器中的组件。

<!--  指定Spring去扫描哪些包

如果这些包及其子包下面的类有@Service等标签,则把其放到容器管理

base-package中如果有多个包可使用逗号(,)或分号(;)隔开。要加入context命名空间-->

<context:component-scanbase-package="com.maple.annotation1,com.maple.annotation2"/>

4.容器扩展点及应用

Spring提供了一些接口,让开发者通过这些接口可以扩充容器的功能。如常用的BeanFactoryPostProcessorBeanPostProcessor口。

4.1自定义类型编辑器

参见1.6  2)和3)。

4.2 BeanFactoryPostProcessor接口(创建Bean前)

实现了该接口的子类要重写其postProcessBeanFactory方法,该方法在配置信息加载完以后,Bean对象初始化之前调用,可以在创建Bean之前过滤信息。

1)写一个类实现该接口,并重写postProcessBeanFactory方法。

publicclassMyBeanFactoryPostProcessorimplementsBeanFactoryPostProcessor {
@Override//该方法在配置信息加载完以后,Bean对象初始化之前调用
publicvoidpostProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)throws BeansException {
          //可以在创建Bean之前过滤信息
System.out.println("该方法在配置信息加载完以后,Bean对象初始化之前调用");
      
}

2)在配置文件注册该类。

<!-- 注册自定义BeanFactoryPostProcessor子类,该类在所有的Bean创建之前都会执行其postProcessBeanFactory方法(只执行一次) -->
<beanclass="com.maple.spring.customtype.MyBeanFactoryPostProcessor"/>

4.3 BeanPostProcessor接口(创建Bean之后)

1)写一个类实现该接口,并重写postProcessBeforeInitialization方法

和postProcessAfterInitialization方法。

publicclassMyBeanPostProcessorimplements BeanPostProcessor{
       @Override//该方法会在每个Bean创建好之后执行,执行完接着执行postProcessAfterInitialization方法
       public ObjectpostProcessBeforeInitialization(Object bean, String beanName)throws BeansException {
          System.out.println("处理Bean前...");
          return bean;//处理完后要返回原来的bean
      
       @Override
       public ObjectpostProcessAfterInitialization(Object bean, String beanName)throws BeansException {
          System.out.println("处理Bean后...");
          return bean;//处理完后要返回原来的bean
      
}

 2) 在配置文件注册该类。

<!--  注册自定义BeanPostProcessor子类,该类在每个Bean创建好都会执行其
postProcessBeforeInitialization方法和postProcessAfterInitialization方法 (会执行多次) -->
<beanclass="com.maple.spring.customtype.MyBeanPostProcessor"/>

4.4使用Spring来加载配置文件

<!-- 加载properties类型的文件的内容 -->
<beanclass="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
          <propertyname="location"value="db.properties"/>
       </bean>
       <!-- 下面是简化的方案 -->
       <context:property-placeholderlocation="db.properties"/>
       <!-- 使用已有的扩展点来扩展一些功能 -->
       <!-- 配置模拟数据源 -->
       <bean  id="dataSource"class="cn.itcast.gz.springioc.MyDataSource">
          <propertyname="url"value="${db.url}"/>
          <propertyname="driverClass"value="${db.driverClass}"/>
          <propertyname="username"value="${db.username}"/>
          <propertyname="password"value="${db.password}"/>
      </bean>

说明:使用了context:property-placeholder后,Spring可以是根据${xxx}可以的内容到xxx.properties文件中找对应的值并设置到value上。

5. 工厂Bean(FactoryBean)

FactoyBean(接口)是一个专用于生产其它类型业务组件的Bean,用于生产其它产品的Bean。

注意:FactoyBeanBeanFactoy是两个完全不同的接口。前者专门用来生产Bean,后者专门用来获取Bean。

1) 自定义工厂Bean

自定义工厂Bean要实现FactoryBean接口。

publicclass PersonFactoryBeanimplementsFactoryBean<Person> {
       @Override
       publicgetObject() throws Exception {
          System.out.println("getObject..........");
          //返回一个Person类型的Bean
          returnnew Person("张三");
      
       @Override
       publicClass<Person> getObjectType() {
          //返回该Bean的类型
          System.out.println("getObjectType..........");
          return Person.class;
      
       @Override
       publicboolean isSingleton() {
          //该Bean是否是单例的
          System.out.println("isSingleton..........");
          returnfalse;
      
}

2) 在配置文件注册该类。

<!-- 注册自定义工厂Bean  根据id值pfb获取的bean的类型不再是PersonFactoryBean类型,而是由PersonFactoryBean类中的getObject方法返回值的类型决定-->
<bean
id="pfb"class="com.maple.spring.customtype.PersonFactoryBean"/>

6. 简化配置(p和c命名空间)

p和c命名空间是Spring3.0引入的,为了简化书写。
p:属性 property
c:构造函数 constructor
c:parent-ref=”p5”引用其他bean。
       <!-- 使用了p和c命名空间
       要在声明schema时加入
      
       <bean  id="p2"class="com.maple.spring.customtype.Person"
 p:name="李四" p:age="23"p:parent-ref="p3"/>
       <bean  id="p3"class="com.maple.spring.customtype.Person"
c:name="王五"/>

7. 代理模式(理解AOP原理的基础)

7.1概念

代理模式的英文叫做Proxy或Surrogate,中文都可译为”代理“,所谓代理,就是一个人或者一个机构代表另一个人或者另一个机构采取行动。在一些情况下,一个客户不想或者不能够直接引用一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。

代理模式包含抽象主题代理主题真实主题

7.2使用JDK创建代理类

使用JDK的Proxy类创建代理对象时,只能创建实现了某个接口的类的代理对象,没有实现接口的类是不能用JDK的Proxy类来创建代理对象的。

方式1:

/*
           * 创建实现了IHello接口的类的代理对象
           * 参数1:类加载器
           * 参数2:接口的字节码
           * 参数3:回调函数(每次调用代理对象非final修饰方法时都会执行里面的invoke方法)
          
           IHello hello = (IHello) Proxy.newProxyInstance(TestProxy.class.getClassLoader(),new Class[]{IHello.class},newInvocationHandler() {
@Override
public Object invoke(Objectproxy, Method method, Object[] args)throws Throwable {
System.out.println("执行invoke方法----" +method.getName());
                     returnnull;
                }
          });
           System.out.println(hello);//null
           System.out.println(hello.getClass());//class $Proxy4
            hello.sayHi();//无内容

方式2:

final IHello realObject=new HelloImpl();//真实对象
          //代理对象
          IHellohello = (IHello) Proxy.newProxyInstance(realObject.getClass().getClassLoader(),realObject.getClass().getInterfaces(),newInvocationHandler() {
@Override
public Object invoke(Objectproxy, Method method, Object[] args)throws Throwable {
                     System.out.println("调用"+method.getName() +"前");
                     Objectret = method.invoke(realObject, args);//在真实对象上调用方法,参数不变照传
                     System.out.println("调用"+method.getName() +"后");
                     return ret;//返回执行方法回的对象,为真实对象
                }
          });
          System.out.println(hello);
          hello.sayHi();//这里是真实对象调用方法了,可以输出"大家好!"
      }

方式3:

publicclass TestProxy2implements InvocationHandler{
       private ObjectrealObject;
       /**
        * @param realObject
        * @return代理对象
      
       public Object createProxyObjectByJdk(ObjectrealObject) {
          this.realObject = realObject;
          return Proxy.newProxyInstance(realObject.getClass().getClassLoader(),realObject.getClass().getInterfaces(),this);
      
       @Override
       publicObjectthrows Throwable {
          System.out.println("调用"+method.getName() +"前");
          Objectret = method.invoke(realObject, args);
          System.out.println("调用"+method.getName() +"后");
          returnret;
      
}

7.3使用CGLIB创建代理类

使用JDK自带的Proxy类不能创建没有实现接口的类的代理对象,这时就要用到第三方类库来创建了。如CGLIB。CGLIB是一个强大的,高性能,高质量的Code生成类库。它可以在运行期扩展Java类与实现Java接口。(要加入jar包:cglib-nodep-xx.xx.jar)

使用CGLIB的Enhancer类来创建代理对象。

publicclass TestProxy {
       @Test//创建类的代理对象
       publicvoidtestCglibProxyClass() {
          //真实对象,没有实现任何接口的类
          final HelloImpl2readObject =new HelloImpl2();
          Enhanceren = new Enhancer();
          //设置回调函数
          en.setCallback(newInvocationHandler() {
                
                @Override
public Objectinvoke(Object proxy, Method method, Object[] args)throws Throwable {
returnmethod.invoke(readObject, args);//调用真实对象的方法并返回
                }
          });
          en.setSuperclass(HelloImpl2.class);//设置父类
          HelloImpl2hello2 = (HelloImpl2) en.create();//创建类的代理对象
          hello2.sayHi();
      
       @Test//创建接口的代理对象
       publicvoidtestCglibProxyInterface() {
          final IHello readObject=new HelloImpl();
          Enhanceren = new Enhancer();
          //设置回调函数
          en.setCallback(newInvocationHandler() {
                @Override
                public Objectinvoke(Object proxy, Method method, Object[] args)throws Throwable {
returnmethod.invoke(readObject, args);//调用真实对象的方法并返回
                }
          });
          en.setSuperclass(IHello.class);//设置父类
          IHellohello = (IHello) en.create();//创建接口的代理对象
          hello.sayHi();
      
}

7.4代理总结

spring在运行期创建代理,不需要特殊的编译器。

spring有两种代理方式:

1.若目标对象实现了若干接口,spring使用JDK的java.lang.reflect.Proxy类代理。

2.若目标对象没有实现任何接口,spring使用CGLIB库生成目标对象的子类。

使用该方式时需要注意:

1.对接口创建代理优于对类创建代理,因为会产生更加松耦合的系统。

对类代理是让遗留系统或无法实现接口的第三方类库同样可以得到通知,这种方式应该是备用方案。

2.标记为final的方法不能够被通知。spring是为目标类产生子类。任何需要被通知的方法都被复写,将通知织入。final方法是不允许重写的。