培训第2个月的第9天----spring的包扫描

               我们知道可以用spring来管理bean,使我们不需要在new 对象,而是从IOC容器中直接获取。这样避免了在Java代码中还要考虑类与类之间的依赖。我们只需要将类交给spring框架,并告诉框架类与类之间的依赖关系,这样我们只需要在类中定义对象属性即可,在加载spring核心配置文件applapplicationContext 后,框架会帮我们将类中的属性自动填充(单例对象),如果是多例对象,那么会在调用getBean()方法时,将对象属性自动填充。

              下面我们就进行简单的spring 的包扫描的测试。

一.applicationContext文件

               我们可以用xml的方式来配置bean,但是如果bean的数量太多,那么就会显得xml非常的繁重,也不利于代码的维护。为了解决这种问题,用扫描包(注解)来配置bean的方式应运而生。我们只需要简单的在applicationContext文件中扫描所需要扫描的包,框架就会将包中被组件注解修饰的类放到IOC容器中,有框架来进行管理。注意,如果设置了类为单例,那么这个类的对象只会有一个,并且只要IOC容器创建了,这个对象创建了。而多例就不一样了,每当调用getBean()方法时,容器才会创建对象,并且这些对象每一个都不相同。

               下面是扫描包的代码(就一条),但是我们要导入相应的约束,要不会出现未定义这个标签的错误。

<?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-3.2.xsd
			  http://www.springframework.org/schema/context
                 http://www.springframework.org/schema/context/spring-context-4.2.xsd"
			

>
<!-- 用spring扫描包,将包下的所有被组建注解修饰的类,都由spring来管理
               创建对象的时候用getbean()方式来获取。
     
             我们都是使用XML的bean定义(bean标签)来配置组件(映射类)。在一个稍大的项目中,通常会有上百个组件,如果这些组件
             采用XML的bean定义来配置,显然会增加配置文件的体积,查找及维护起来也不太方便。
    Spring2.5为我们引入了组件自动扫描机制,它可以在类路径(也就是类所在的包)底下寻找标注了@Component、@Servi
    ce、@Controller、@Repository注解的类 ,并把这些类纳入进Spring容器(IOC容器)中管理。
             它的作用和在XML文件中使用bean节点(bean标签)配置组件是一样的。
             其中<context:component-scan base-package="cn.itcast" />这个配置隐式注册了(配置了)多个对注解
             进行解析处 理的处理器,包括<context:annotation-config/>该配置注册的处理器,也就是说写了<context:com
    ponent-scan base-package="" />配置,就不用写<context:annotation-config/>配置了,此外base
    -package为需要扫描的包(含子包)。
    @Service用于标注业务层组件(类,bean)、 @Controller用于标注控制层组件(如Struts2中的action)、@Repository用于
             标注数据访问组件,即DAO组件。而@Component泛指组件,当组件不好归类的时候,我们可以使用这个注解进行标注。 
    
    
          
 -->
    

    <!-- 要使用自动扫描机制,我们需要打开以下配置信息 -->
    <context:annotation-config></context:annotation-config>
    <context:component-scan base-package="com.java" use-default-filters="true">
        <!-- 只扫描这个包下的Controller注解 
    	<context:include-filter type="annotation" expression="org.springframework.stereotype.Service"></context:include-filter>
    	-->
    	<!-- 如果不想扫描哪个包,就是用exclude 
    	<context:exclude-filter type="annotation" expression="com.java.serviceImpl.UserServiceImpl"></context:exclude-filter>
    	-->
    </context:component-scan>
    
    


</beans>

                  其中的 include标签和 exclude标签的功能会使扫描包变得更加的”随心所欲“。

二.service层的代码

                  这里的service接口我就不贴代码了,直接贴实现类ServiceImlp的代码:

package com.java.DaoImpl;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;

import org.springframework.stereotype.Repository;

import com.java.Dao.UserDao;
@Repository
public class UserDaoImpl implements UserDao {

	@Override
	public void save() {
		System.out.println("UserDaoImpl 实现类中的  save()方法被调用");
		
	}

	public UserDaoImpl() {
		super();
		System.out.println("UserDaoImpl 的无参构造 方法被调用");
	}
	@PostConstruct
	public void init111() {
		System.out.println("UserDaoImpl 实现类中的 init111() 方法被调用");
	}
	
	@PostConstruct
	public void init222() {
		System.out.println("UserDaoImpl 实现类中的 init222() 方法被调用");
	}
	@PreDestroy
	public void destroy111() {
		System.out.println("UserDaoImpl 实现类中的 destroy111() 方法被调用");
	}
	@PreDestroy
	public void destroy222() {
		System.out.println("UserDaoImpl 实现类中的 destroy222() 方法被调用");
	}

}

三.dao层的代码

                同理,这里的dao接口代码也不贴了,我们直接来看实现类DaoImpl的代码:

package com.java.DaoImpl;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;

import org.springframework.stereotype.Repository;

import com.java.Dao.UserDao;
@Repository
public class UserDaoImpl implements UserDao {

	@Override
	public void save() {
		System.out.println("UserDaoImpl 实现类中的  save()方法被调用");
		
	}

	public UserDaoImpl() {
		super();
		System.out.println("UserDaoImpl 的无参构造 方法被调用");
	}
	@PostConstruct
	public void init111() {
		System.out.println("UserDaoImpl 实现类中的 init111() 方法被调用");
	}
	
	@PostConstruct
	public void init222() {
		System.out.println("UserDaoImpl 实现类中的 init222() 方法被调用");
	}
	@PreDestroy
	public void destroy111() {
		System.out.println("UserDaoImpl 实现类中的 destroy111() 方法被调用");
	}
	@PreDestroy
	public void destroy222() {
		System.out.println("UserDaoImpl 实现类中的 destroy222() 方法被调用");
	}

}

四.获取sessionfactory的工具类

                 这个工具类是有Myeclipse自动生成的:

package com.java.Utils;

import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.cfg.Configuration;

/**
 * Configures and provides access to Hibernate sessions, tied to the
 * current thread of execution.  Follows the Thread Local Session
 * pattern, see {@link http://hibernate.org/42.html }.
 */
public class HibernateSessionFactory {

    /** 
     * Location of hibernate.cfg.xml file.
     * Location should be on the classpath as Hibernate uses  
     * #resourceAsStream style lookup for its configuration file. 
     * The default classpath location of the hibernate config file is 
     * in the default package. Use #setConfigFile() to update 
     * the location of the configuration file for the current session.   
     */
    private static String CONFIG_FILE_LOCATION = "hibernate.cfg.xml";
	private static final ThreadLocal<Session> threadLocal = new ThreadLocal<Session>();
    private  static Configuration configuration = new Configuration();    
    private static org.hibernate.SessionFactory sessionFactory;
    private static String configFile = CONFIG_FILE_LOCATION;

	static {
    	try {
			configuration.configure(configFile);
			sessionFactory = configuration.buildSessionFactory();
		} catch (Exception e) {
			System.err
					.println("%%%% Error Creating SessionFactory %%%%");
			e.printStackTrace();
		}
    }
	/*私有化构造方法优先思考单例设计模式*/
    private HibernateSessionFactory()
    {
    	
    }
	
	/**
     * Returns the ThreadLocal Session instance.  Lazy initialize
     * the <code>SessionFactory</code> if needed.
     *
     *  @return Session
     *  @throws HibernateException
     */
    public static Session getSession() throws HibernateException 
    {
    	/*确保当前线程中session的唯一性*/
        Session session = (Session) threadLocal.get();
        /*如果当前线程中session是空的或者为关闭的*/
		if (session == null || !session.isOpen()) 
		{
			/*构建一个新的session*/
			/*由于session是由sessionFactory产生的,因此需要判断sessionFactory的状态*/
			if (sessionFactory == null) 
			{
				/*重新读取一次配置文件进行构建sessionFactory*/
				rebuildSessionFactory();
			}
			session = (sessionFactory != null) ? sessionFactory.openSession(): null;
			/*再次放到线程中	*/
			threadLocal.set(session);
		}

        return session;
    }

	/**
     *  Rebuild hibernate session factory
     *
     */
	public static void rebuildSessionFactory() {
		try {
			configuration.configure(configFile);
			sessionFactory = configuration.buildSessionFactory();
		} catch (Exception e) {
			System.err
					.println("%%%% Error Creating SessionFactory %%%%");
			e.printStackTrace();
		}
	}

	/**
     *  Close the single hibernate session instance.
     *
     *  @throws HibernateException
     */
    public static void closeSession() throws HibernateException {
        Session session = (Session) threadLocal.get();
        threadLocal.set(null);

        if (session != null) {
            session.close();
        }
    }

	/**
     *  return session factory
     *
     */
	public static org.hibernate.SessionFactory getSessionFactory() {
		return sessionFactory;
	}

	/**
     *  return session factory
     *
     *	session factory will be rebuilded in the next call
     */
	public static void setConfigFile(String configFile) {
		HibernateSessionFactory.configFile = configFile;
		sessionFactory = null;
	}

	/**
     *  return hibernate configuration
     *
     */
	public static Configuration getConfiguration() {
		return configuration;
	}

}

                    我将自动生成的简化了一下,所谓的简化,也就是简化了1,2行。。。。。   如下:

package com.java.Utils;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;

//获取session的工具类
public class HibernateSessionFactory_i {
    //静态变量
	private static String CONFIG_FILE_LOCATION="/hibernate_cache.cfg.xml";
	private static Configuration configuration=new Configuration();
	private static SessionFactory sessionFactory;
	private static String configFile=CONFIG_FILE_LOCATION;
	private static final ThreadLocal<Session> threadLocal=new ThreadLocal<Session>();
	
	//为变量进行初始化
	static {	
		configuration.configure(configFile); //读取配置文件
		sessionFactory=configuration.buildSessionFactory();  //创建sessionFactory
	}
  
	private HibernateSessionFactory_i() {
   	 //私有化构造方法,首先想到的是单例设计模式
   }
	 
	public static Session getSession(){
		Session session=threadLocal.get();
		
		if(session==null || !session.isOpen()) {
			
			//为了减少异常,要判断SessionFactory是否为空
			if(sessionFactory==null) {
				//重新读取一次配置文件,来获得SessionFactory对象
				rebuildSessionFactory();
			}
			session=(sessionFactory!=null) ? sessionFactory.openSession():null;
			//这一句话是相当重要的,通过这句话才使session是一个单例的模式,通过这个对象来获得session。
			threadLocal.set(session);
		}
		return session;
	}
	
	public static void rebuildSessionFactory() {
		configuration.configure(configFile);
		sessionFactory = configuration.buildSessionFactory();
	}
	
	public static void closeSession() {
		Session session=threadLocal.get();
		threadLocal.set(null);
		if(session!=null) {
			session.close();
		}
	}
	
	public static Configuration getConfiguration() {
		return configuration;
	}
	
	public static SessionFactory getSessionFactory() {
		return sessionFactory;
	}
	
	public static void setConfigFile(String configFile) {
		//更改配置文件路径,并将依赖配置文件生成的SessionFactory置为null
		HibernateSessionFactory_i.configFile = configFile;
		sessionFactory = null;
	}
}

五.测试类

               这是重点。。。。

               先来一个测试,看看输出什么结果。

扫描二维码关注公众号,回复: 3034237 查看本文章
package com.java.text;


import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.java.DaoImpl.UserDaoImpl;

import com.java.serviceImpl.UserServiceImpl;

public class Text1 {
   public static void main(String[] args) {
	   //读取spring的核心配置文件
	   ApplicationContext applicationContext=new ClassPathXmlApplicationContext("applicationContext.xml");
	   //通过Autowired自动装配的到对象
	   UserServiceImpl userServiceImpl = (UserServiceImpl)applicationContext.getBean("userService");
	   UserServiceImpl userServiceImpl2 = (UserServiceImpl)applicationContext.getBean("userService");
	   UserDaoImpl userDaoImpl = (UserDaoImpl)applicationContext.getBean("userDaoImpl");
	   UserDaoImpl userDaoImpl2 = (UserDaoImpl)applicationContext.getBean("userDaoImpl");
	   userServiceImpl.text();
	   //打印对象地址
	   System.out.println(userServiceImpl);
	   System.out.println(userServiceImpl2);
	   System.out.println(userDaoImpl);
	   System.out.println(userDaoImpl2);
	   //关闭容器,也就是销毁了IOC容器中的Bean对象

	   
	   
	    
   }
   
   public void text() {
	   /*1.由于spring3.2版本不支持jdk1.8,所以在进行测试的时候,会出现异常,所以将spring版本变成5.0.2版本,
	    *那么问题就解决了。
	    *2.注意,在用getBean()方法获得由扫描包的方式装配的bean时,参数一定要是组件注解修饰的类的类名的首字母小写。
	    *否则会发生异常 No bean named 'UserServiceImpl' available。当然如果我们就想自定义名称,那也是
	    *可以的,只需要在类上面的注解旁添加自定义的名称即可。
	    *3.用扫描的方式将类变成bean放到IOC容器中的过程:先读取spring的核心配置文件,找到标签扫描包,将由组件注解的
	    *类变成bean放到ioc容器中,通过Autowired注解,给其修饰的对象属性赋值,赋值的对象从IOC容器中取。
	    *4.如果对象属性不添加Autowired的注解,那么是没办法从IOC容器取出对象给属性。
	    *5.xml文件配置bean和扫描包(注解)配置bean默认都是单例的模式,也就是在IOC容器创建的时候,那么这个对象就已
	    *经存在于容器中了,不管你使不使用它。IOC容器会管理作用域为Singleton对象的整个生命周期(这个对象会一直由I
	    *OC来管理),包括创建和销毁。
	    *6.既然扫描包(注解)配置bean默认都是单例的模式,但是又不像xml文件配置bean方式的那样,可以通过bean标签的
	    *scope属性来设置属性值为prototype来使单例模式变为多例。但是我们可以在组件注解的旁边添加一个 @Scope()来
	    *更改单例模式变为多例(更改bean的作用域从singleton到prototype)。
	    *7.简单来说就是<context:include-filter>标签用于指定你想扫描的类和注解。而<context:exclude-fi
	    *lter>标签用于只定你不想扫描的类和注解。但是这两者的前提条件是use-dafault-filters=false 的情况下的,
	    *如果不设置为false,那么过滤器就相当于没设置一样。
	    *8.进行了一个测试,如果一个标签中有一个默认属性。例如use-dafault-filters=true,这是隐式的,我们看不到,
	    *我们如果想将这个属性置为false就直接可以写use-dafault-filters=false。但是,既然隐式的true,那么我
	    *们显示的设为true可不可以呢?也就是显示的写出use-dafault-filters=true这句话,答案是可以的。
	    *9.若是用扫描(注解)的方式把组建放入IOC中来管理,我们如何指定bean的初始化方法和销毁方法呢?这就需要我们用到
	    *@PostConstruct和@PreDestroy这两个注解了。这里初始化的方法和方法名没有关系,和方法名上的注解又关系,
	    *方法上面用@PostConstruct注解修饰,那么这个方法就是初始化容器中bean时的初始化方法,而且验证了,初始化方法
	    *的数量可以又多个。但是在调用初始化方法之前,会先调用类的构造方法。
	    *10.注意,如果bean是单例的,那么它会随着IOC容器的创建而创建,即使你不用这个对象,那么这个对象在创建的时候仍会
	    *调用它的初始化方法。但是,一旦这个bean是多例的,那么只有在创建这个对象(bean)的时候才调用初始化方法。(这里先
	    *做一个小小的猜测,创建对象,是先给最"内层"的属性赋值)。
	    *
	    * */
   }
}

              输出的结果为:

log4j:WARN No appenders could be found for logger (org.springframework.core.env.StandardEnvironment).
log4j:WARN Please initialize the log4j system properly.
log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info.
UserDaoImpl 的无参构造 方法被调用
UserDaoImpl 实现类中的 init111() 方法被调用
UserDaoImpl 实现类中的 init222() 方法被调用
UserServiceImpl 的无参构造 方法被调用
UserServiceImpl 的 init()方法被调用
UserServiceImpl 被访问
com.java.DaoImpl.UserDaoImpl@add0edd
UserDaoImpl 实现类中的  save()方法被调用
com.java.serviceImpl.UserServiceImpl@23f7d05d
com.java.serviceImpl.UserServiceImpl@23f7d05d
com.java.DaoImpl.UserDaoImpl@add0edd
com.java.DaoImpl.UserDaoImpl@add0edd

              结论我已经在测试类中以注释的形式显示在类中,这里就不再写一次了。对应上面的代码和输出信息,应该就能知道代码是怎么执行的。

              如果有那么一丝疑问,那么我们把测试类改成如下这样,在来测试一下:

package com.java.text;


import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.java.DaoImpl.UserDaoImpl;

import com.java.serviceImpl.UserServiceImpl;

public class Text1 {
   public static void main(String[] args) {
	   //读取spring的核心配置文件
	   ApplicationContext applicationContext=new ClassPathXmlApplicationContext("applicationContext.xml");
	  
	   
	   
	    
   }
   
   public void text() {
	   /*1.由于spring3.2版本不支持jdk1.8,所以在进行测试的时候,会出现异常,所以将spring版本变成5.0.2版本,
	    *那么问题就解决了。
	    *2.注意,在用getBean()方法获得由扫描包的方式装配的bean时,参数一定要是组件注解修饰的类的类名的首字母小写。
	    *否则会发生异常 No bean named 'UserServiceImpl' available。当然如果我们就想自定义名称,那也是
	    *可以的,只需要在类上面的注解旁添加自定义的名称即可。
	    *3.用扫描的方式将类变成bean放到IOC容器中的过程:先读取spring的核心配置文件,找到标签扫描包,将由组件注解的
	    *类变成bean放到ioc容器中,通过Autowired注解,给其修饰的对象属性赋值,赋值的对象从IOC容器中取。
	    *4.如果对象属性不添加Autowired的注解,那么是没办法从IOC容器取出对象给属性。
	    *5.xml文件配置bean和扫描包(注解)配置bean默认都是单例的模式,也就是在IOC容器创建的时候,那么这个对象就已
	    *经存在于容器中了,不管你使不使用它。IOC容器会管理作用域为Singleton对象的整个生命周期(这个对象会一直由I
	    *OC来管理),包括创建和销毁。
	    *6.既然扫描包(注解)配置bean默认都是单例的模式,但是又不像xml文件配置bean方式的那样,可以通过bean标签的
	    *scope属性来设置属性值为prototype来使单例模式变为多例。但是我们可以在组件注解的旁边添加一个 @Scope()来
	    *更改单例模式变为多例(更改bean的作用域从singleton到prototype)。
	    *7.简单来说就是<context:include-filter>标签用于指定你想扫描的类和注解。而<context:exclude-fi
	    *lter>标签用于只定你不想扫描的类和注解。但是这两者的前提条件是use-dafault-filters=false 的情况下的,
	    *如果不设置为false,那么过滤器就相当于没设置一样。
	    *8.进行了一个测试,如果一个标签中有一个默认属性。例如use-dafault-filters=true,这是隐式的,我们看不到,
	    *我们如果想将这个属性置为false就直接可以写use-dafault-filters=false。但是,既然隐式的true,那么我
	    *们显示的设为true可不可以呢?也就是显示的写出use-dafault-filters=true这句话,答案是可以的。
	    *9.若是用扫描(注解)的方式把组建放入IOC中来管理,我们如何指定bean的初始化方法和销毁方法呢?这就需要我们用到
	    *@PostConstruct和@PreDestroy这两个注解了。这里初始化的方法和方法名没有关系,和方法名上的注解又关系,
	    *方法上面用@PostConstruct注解修饰,那么这个方法就是初始化容器中bean时的初始化方法,而且验证了,初始化方法
	    *的数量可以又多个。但是在调用初始化方法之前,会先调用类的构造方法。
	    *10.注意,如果bean是单例的,那么它会随着IOC容器的创建而创建,即使你不用这个对象,那么这个对象在创建的时候仍会
	    *调用它的初始化方法。但是,一旦这个bean是多例的,那么只有在创建这个对象(bean)的时候才调用初始化方法。(这里先
	    *做一个小小的猜测,创建对象,是先给最"内层"的属性赋值)。
	    *
	    * */
   }
}

                这样我们在看一下输出结果:

log4j:WARN No appenders could be found for logger (org.springframework.core.env.StandardEnvironment).
log4j:WARN Please initialize the log4j system properly.
log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info.
UserDaoImpl 的无参构造 方法被调用
UserDaoImpl 实现类中的 init111() 方法被调用
UserDaoImpl 实现类中的 init222() 方法被调用
UserServiceImpl 的无参构造 方法被调用
UserServiceImpl 的 init()方法被调用

                      这样就明白是怎么执行了的吧(。。。。。)。

                      在提一嘴:           spring 3.X版本支持到java7 
                                                     spring 4.X版本支持Java8最低支持到Java6 

猜你喜欢

转载自blog.csdn.net/qq_41160264/article/details/82191422