我们知道可以用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;
}
}
五.测试类
这是重点。。。。
先来一个测试,看看输出什么结果。
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