Spring框架的学习(第二天)

xml文件导入其他xml文件配置

如果我们在spring框架中配置了多个xml文件,我们可以在读取配置文件的时候把这些xml文件一下全都读取,也可以只读一个总的xml文件,在这个总的xml文件中把其他的xml全都都导入进来。
例如:

student.xml文件:

<bean name="student" class="com.briup.bean.Student">
    <property name="id">
        <value>25</value>
    </property>
</bean>

teacher.xml文件:

<bean name="teacher" class="com.briup.bean.Teacher">
        <property name="student" ref="student"></property>//ref=""手动注入哪个bean
</bean>

import.xml文件:

<import resource="teacher.xml"/>
<import resource="student.xml"/>

main:

String[] path = {"com/briup/ioc/imp/import.xml"};
ApplicationContext container = new ClassPathXmlApplicationContext(path);

Teacher t = (Teacher) container.getBean("teacher");
System.out.println(t.getStudent());

创建bean实例的方式

xml文件中有bean的配置,而且这个bean所对应的java类中存在一个无参构造器,那么这个时候spring容器就可以使用反射调用无参构造器来创建实例了

通过工厂类获得实例(工厂类实现了接口FactoryBean<?>)

注意spring中的PropertyPlaceholderConfigurer类的使用,在htmlsingle中直接搜索类名即可

例如:
工厂类实现指定接口并且实现接口中的三个抽象方法:

    public class ConnectionFactory implements FactoryBean<Connection>{
    private String driver;
    private String url;
    private String username;
    private String password;

    @Override
    public Connection getObject() throws Exception {
        Class.forName(driver);
        Connection conn = 
            DriverManager.getConnection(url,username,password);
        return conn;
    }

    @Override
    public boolean isSingleton() {//判断它是否是单例
        // TODO Auto-generated method stub
        return false;
    }
    
    @Override
    public Class<Connection> getObjectType() {
        // TODO Auto-generated method stub
        return Connection.class;
    }
    set/get
    ....
  }

xml文件:

因为这个类是一个工厂类,所以我们用名字conn在容器中拿对象的时候,拿到并不是这个工厂类对象,而是这个工厂类对象调用完工厂方法后所返回的对象.

  <bean name="conn" class="com.briup.ioc.factory.ConnectionFactory">
    <property name="driver">
        <value>${driver}</value>//从一个配置文件中以key—value的形式拿value
    </property>
    
    <property name="url">
        <value>${url}</value>
    </property>
    
    <property name="username">
        <value>${username}</value>
    </property>
    
    <property name="password">
        <value>${password}</value>
    </property>
  </bean>
  
  <!-- 
    下面配置的这个类,可以自动的帮我们去读取指定的properties文件的
    内容,文件中用key-value的形式存放数据,读完之后我们就可以用
    ${key}这种形式去拿文件中的value值了。
    classpath指的是从src下面找.
  -->
  <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
    <property name="location">
        <value>classpath:oracle.properties</value>
    </property>
  </bean>

main:

String path = "com/briup/ioc/factory/factory.xml";
ApplicationContext container = 
        new ClassPathXmlApplicationContext(path);
Connection conn = (Connection) container.getBean("conn");
System.out.println(conn);

通过实例工厂获得实例(不需要实现或者继承任何接口或者父类)

注意spring中的PropertyPlaceholderConfigurer类的使用,在htmlsingle中直接搜索类名即可

例如:一个普通的工程类

  public class ConnectionFactory{
    private String driver;
    private String url;
    private String username;
    private String password;
    
    public Object getConnection() throws Exception {
        Class.forName(driver);
        Connection conn = DriverManager.getConnection(url,username,password);
        return conn;
    }
    get/set
    ....
  }

  xml文件:
 <bean name="factory" class="com.briup.ioc.instanceFactory.ConnectionFactory">
    <property name="driver">
        <value>${driver}</value>
    </property>
    
    <property name="url">
        <value>${url}</value>
    </property>
    
    <property name="username">
        <value>${username}</value>
    </property>
    
    <property name="password">
        <value>${password}</value>
    </property>
 </bean>
    
 <!-- 
    将来通过这个conn来拿对象,拿到的是名字为factory的工厂类调用完
    名字为getConnection方法之后所返回的对象。
 -->
 <bean name="conn" factory-bean="factory" factory-method="getConnection"></bean>
 
 <!-- 读取properties文件 -->
 <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
    <property name="location">
        <value>classpath:oracle.properties</value>
    </property>
 </bean>

main:

String path = "com/briup/ioc/instanceFactory/instanceFactory.xml";
ApplicationContext container = 
    new ClassPathXmlApplicationContext(path);
Connection conn = (Connection) container.getBean("conn");
System.out.println(conn);

通过静态工厂获得实例

例如:含义静态方法的工厂类

  public class ConnectionFactory{
    private static String driver = "oracle.jdbc.driver.OracleDriver";
    private static String url = "jdbc:oracle:thin:@127.0.0.1:1521:XE";
    private static String username = "briup";
    private static String password = "briup";
    
    public static Object getConnection() throws Exception {
        Class.forName(driver);
        Connection conn = DriverManager.getConnection(url,username,password);
        return conn;
    }
  }

  xml文件:
  <!-- 这样配置一定要求getConnection方法是静态方法 -->
  <bean name="conn" class="com.briup.ioc.staticFactory.ConnectionFactory" factory-method="getConnection"></bean>

main:

String path = "com/briup/ioc/staticFactory/staticFactory.xml";
ApplicationContext container = new ClassPathXmlApplicationContext(path);
Connection conn = (Connection) container.getBean("conn");
System.out.println(conn);

自定义属性编辑器 PropertyEditor

Spring中我们可以使用属性编辑器来将特定的字符串转换为对象 :String-->object java.beans.PropertyEditor(JDK中的接口)用于将xml文件中字符串转换为特定的类型
同时JDK为我们提供一个实现类java.beans.PropertyEditorSupport
Spring在注入时,如果遇到类型不一致(例如需要Address类型但是用户传了个String)则会去调用相应的属性编辑器进行转换.
spring会调用属性编辑器的setAsText(String str)进行处理用户传的字符串,并调用getValue()方法获取处理后得到的对象,所以我们在代码中处理完后记得调用setValue方法,要不然spring调用getValue方法拿不到你处理后的对象

自定义属性编辑器示例

  1. //自定义编辑器类

     public class AddressEditor extends PropertyEditorSupport {
     @Override
     public String getAsText() {
         return super.getAsText();
     }
    
  2. //Spring遇到数据类型不一致并且不能自己处理的时候会调用这个方法处理字符串

     @Override
     public void setAsText(String text) throws IllegalArgumentException {
         String[] str = text.split(",");
         String city = str[0];
         String street = str[1];
         String country = str[2];
         Address add = new Address(city, street, country);
         setValue(add);
     }
    
     }
     //Address类
     public class Address {
     private String city;
     private String street;
     private String country;
     set/get
     .....
     }
    
     //Student类
     public class Student {
     private long id;
     private String name;
     private boolean gender;
     private int age;
     private Address address;
     get/set
     ...
     }
    

xml文件:

    <!-- 这个配置指明哪个类型对应哪个自定义编辑器 -->
    <bean class="org.springframework.beans.factory.config.CustomEditorConfigurer">
    <property name="customEditors">
     <map>
    <entry key="com.briup.ioc.proEdit.Address"  value="com.briup.ioc.proEdit.AddressEditor"/>
</map>
    </property>
    </bean>
 <!-- spring发现address的值不能注入的时候(类型不对),就会调用对应的属性编辑器处理了 -->
  <bean id="student" class="com.briup.ioc.proEdit.Student">
    <property name="id" value="1"/>
    <property name="name" value="tom"/>
    <property name="age" value="45"/>
    <property name="gender" value="true"/>
    <property name="address">
        <value>kunshan,xueyuan,China</value>
    </property>
 </bean>

自定义事件

在spring中我们可以自定义事件,并且可以使用ApplicationContext类型对象(就是spring容器container)来发布这个事件,事件发布之后,所有的ApplicaitonListener(监听器)实例都会被触发并调用指定方法onApplicationEvent()来处理.

例如:
自定义事件类RainEvent:

public class RainEvent extends ApplicationEvent {
public RainEvent(Object source) {
    super(source);
}
}

监听器类RainListener1

public class RainListener1 implements ApplicationListener {
//这里需要加类型判断,如果不加类型判断的话只要是事件发生都会执行监听器类这个
//方法,所以我们必须加上类型判断,当属于我们自定义的那个类型的时候我们才进行处理。
public void onApplicationEvent(ApplicationEvent event) {
    if (event instanceof RainEvent) {
        System.out.println("唐僧大喊:" + event.getSource() + "赶快收衣服喽!");
    }
 }

}
监听器类RainListener2

public class RainListener2 implements ApplicationListener {
public void onApplicationEvent(ApplicationEvent event) {
    if (event instanceof RainEvent) {
        System.out.println("我们:" + event.getSource() + "太好了不用上课了!");
    }
 }

}

xml文件:

<!-- 只需要把这俩个监听器类交给spring容器管理就可以了 -->
<bean class="com.briup.ioc.event.RainListener1"></bean>
<bean class="com.briup.ioc.event.RainListener2"></bean>

main:

String path = "com/briup/ioc/event/event.xml";
ApplicationContext container = new ClassPathXmlApplicationContext(path);
//触发条件
container.publishEvent(new RainEvent("下雨了!"));

ioc中的annotation配置

  1. @Autowired

  2. @Autowired默认按照byType匹配的方式进行注入,如果没有一个bean的类型是匹配的则会抛异常,
    如果有多个bean的类型都匹配成功了,那么再按byName方式进行选择

  3. @Autowired注解可以写在成员变量、set/ger方法、构造器函数上面

  4. @Autowired如果最终匹配不成功(注意一定是一个都没有找到的情况)则会抛出异常,
    但是如果设置为@Autowired(required=false),则最终匹配不成功没有不会抛出异常。

  5. @Autowired可以结合 @Qualifier("beanName")来使用,则可以达到byName的效果

  6. @Autowired使用后需要在xml文件加入以下配置才能生效:
    <context:annotation-config/>(高版本的就不需要加 )

  7. @Resource

  8. @Resource的作用和 @Autowired差不多,只不过@Resource是默认先用byname,
    如果找不到合适的就再用bytype来注入

  9. Resource有俩个属性,name和type,使用name属性则表示要byName匹配,使用type属性则表示要byType匹配

  10. @Resource使用后需要在xml文件加入以下配置才能生效:
    <context:annotation-config/>

  11. @PostConstruct 和 @PreDestroy

单例的类是有ApplicationContext管理。所以当ac.destory()时,类也就销毁了。

  1. 标注了 @PostConstruct 注释的方法将在类实例化后调用。
  2. 标注了 @PreDestroy 的方法将在类销毁之前调用。
  1. @Component

这个注释相当于这样的内容。<bean name="student" class="com.zts.ioctest.Student" scope="prototype">

  1. @Component注解可以直接定义bean,而无需在xml定义。但是若两种定义同时存在,xml中的定义会覆盖类中注解的Bean定义。
  2. @Component注解直接写在类上面即可
  3. @Component有一个可选的参数,用于指定 bean 的名称:@Component("boss")
  4. @Component容易不指定参数,则 bean 的名称为当前类的类名小写
  5. @Component使用之后需要在xml文件配置一个标签: <context:component-scan/>
  6. <context:component-scan base-package="com.briup.ioc.annotation" /> 可以表示spring需要检查哪个包下的java类,看它们是否使用了 @Component注解
  7. @Component定义的bean默认情况下都是单例模式的,如果要让这个bean变为非单例,可以再结合这个 @Scope 注解来达到目标 @Scope("prototype")

@Component是Spring中所有bean组件的通用形式, @Repository @Service @Controller 则是 @Component的细化,用来表示更具体的用例,分别对应了持久化层、服务层和表现层。但是至少到现在为止这个四种注解的实质区别很小(甚至几乎没有),都是把当前类注册为spring容器中的一个bean

注意:
component-scan标签默认情况下自动扫描指定路径下的包(含所有子包),将带有 @Component @Repository @Service @Controller标签的类自动注册到spring容器。
对标记了 Spring中的 @Required @Autowired @PostConstruct @PreDestroy @Resource @WebServiceRef @EJB @PersistenceContext @PersistenceUnit等注解的类进行对应的操作使注解生效(包含了annotation-config标签的作用)。

Spring框架的学习第一天
Spring框架的学习第三天

推荐阅读更多精彩内容