Spring的引出:
问题1:依赖配置信息。。。
class UserDaoImpl implements UserDao { //数据库连接信息,需要一些配置,但是又不能写在这个类中(硬编码,改的时候麻烦) private String jdbcUrl = "..."; private String driverClass; private String username; private String password; // ... }
把这些配置信息放在外部的一个配置文件中:
jdbc.properties ---------------- jdbcUrl = .... driverClass = .... ... ...
问题2:依赖其他的对象。。。
class UserServiceImpl{ //和具体的实现类耦合在一起了 //private UserDao userDao = new UserDaoImpl(); //可以采用工厂的方式解决这个问题 private UserDao userDao = Factory.getUserDao(); }工厂里可以采用反射生成实例
配置文件:
jdbcUrl = jdbc:mysql:///test2 driverClass = com.mysql.jdbc.Driver username = root password = root
public class UserDaoImpl implements UserDao { private String jdbcUrl; private String driverClass; private String username; private String password; // ... public UserDaoImpl() { // 读取配置文件 String resource = "cn/itcast/spring/a_helloworld/jdbc.properties";//一定要加包名 Properties props = loadProperties(resource); // 并初始化信息 jdbcUrl = props.getProperty("jdbcUrl"); driverClass = props.getProperty("driverClass"); username = props.getProperty("username"); password = props.getProperty("password"); } /** * 加载配置文件 */ private Properties loadProperties(String resource) { InputStream inputStream = null; try { //带类加载器(getClassLoader())的情况资源要加包名,因为这种情况是在classpath下面查找。 //cn/itcast/spring/a_helloworld/jdbc.properties inputStream = this.getClass().getClassLoader().getResourceAsStream(resource); Properties props = new Properties(); props.load(inputStream); return props; } catch (IOException e) { throw new RuntimeException(e); } finally { try { inputStream.close(); } catch (IOException e) { throw new RuntimeException(e); } } } }
==================================================================
怎么样才能不依赖实现类呢?
可以使用工厂模式,让UserDao的实现在在工厂中生成,而工厂是可以配置的,如下:
// 一、定义工厂类 public class BeanFactory { // 用于存放“对象名--实现类的全名”这种对应关系的键值对集合 private static Properties properties; static { // 读取配置文件 ObjectFactory.properties,具体代码略。 properties.load(inStream); } // 可创建对象实例的工厂方法,接受的参数是对象名,如UserDao、RoleDao等
public static <T>T getBeanInstance(Class<T> clazz) { try { String className = props.getProperty(clazz.getSimpleName()); return (T)Class.forName(className).newInstance(); } catch (Exception e) { throw new RuntimeException(e); } }
} // 二、使用工厂生成实例 # UserDao = cn.itcast.dao.impl.MySQLUserDaoImpl # UserDao = cn.itcast.dao.impl.OracleUserDaoImpl UserDao = cn.itcast.dao.impl.MySQLUserDaoImpl // 三、使用工厂生成实例 public class UserServiceImpl { private UserDao userDao = (UserDao) BeanFactory.getBeanInstance("UserDao"); // ... }
以上代码就是通过工厂实现的与实现类的解耦,这种情况下如果想要更新别外一个实现类,只需要修改配置文件就可以了。
控制反转(IOC,Inversion of Control)
所谓控制反转就是应用程序本身不负责依赖对象的创建及对象之间关系的维护,而是由外部容器负责的,通过容器来实现对象组件的装配和管理,这样控制权就由应用转移到了外部容器,控制权的转移就是所谓的控制反转。
IoC是一个很大的概念,可以用不同的实现方式来实现。
例如:<1>依赖查找(Dependency Lookup):容器提供回调接口和上下文环境给组件。EJB和Apache Avalon都使用这种方式。
<2>依赖注入(Dependency Injection):组件不做定位查询,只提供普通的Java方法让容器去决定依赖关系。后者是时下最流行的IoC类型。
依赖注入(DI,Dependency Injection)
当我们把依赖对象交给外部容器负责创建,那么PersonServiceBean 类可以改成如下: public class PersonServiceBean { private PersonDao personDao ; // 要有getter与setter // 通过构造器参数,让容器把创建好的依赖对象注入进PersonServiceBean public PersonServiceBean(PersonDao personDao){ this.personDao=personDao; } // 当然也可以使用setter方法进行注入。 public void save(Person person){ personDao.save(person); } }
所谓依赖注入就是指:在运行期,由外部容器动态地将依赖对象注入到组件中。
面向切面编程(AOP,Aspect Oriented Programming)
AOP为Aspect Oriented Programming的缩写,意为:面向切面编程(也叫面向方面),可以通过预编译方式和运行期动态代理实现在不修改源代码的情况下给程序动态统一添加功能的一种技术。主要的功能是:日志记录,性能统计,安全控制,事务处理,异常处理等等。使用JDK的动态代理可以实现AOP,
Spring是一个开源的控制反转(IoC)和面向切面(AOP)的容器框架。它的主要目是简化应用的开发。
1.1. 使用Spring有什么好处
在项目中引入spring立即可以带来下面的好处
l 降低组件之间的耦合度,实现软件各层之间的解耦。
l 可以使用容器提供的众多服务,如:事务管理服务、消息服务等等。当我们使用容器管理事务时,开发人员就不再需要手工控制事务.也不需处理复杂的事务传播。
l 容器提供单例模式支持,开发人员不再需要自己编写实现代码。
l 容器提供了AOP技术,利用它很容易实现如权限拦截、运行期监控等功能。
l 容器提供的众多辅作类,使用这些类能够加快应用的开发,如: JdbcTemplate、 HibernateTemplate。
l Spring对于主流的应用框架提供了集成支持,如:集成Hibernate、JPA、Struts等,这样更便于应用的开发。
===================================================
使用:
applicationContext.xml
<!-- 配置UserDao对象,需要注入一些配置 --> <bean id="userDao" class="cn.itcast.spring.b_springhelloworld.UserDaoImpl"> //name是UserDaoImpl中的属性名 <property name="jdbcUrl" value="jdbc:mysql:///test111"></property> <property name="driverClass" value="com.mysql.jdbc.Driver"></property> <property name="username" value="root"></property> <property name="password" value="root"></property> </bean> <!-- 配置UserService对象,需要依赖UserDao对象 --> <bean id="userService" class="cn.itcast.spring.b_springhelloworld.UserServiceImpl"> <property name="userDao" ref="userDao"></property> </bean>
@Test public void testGetUserDao() { // Spring的容器对象 ApplicationContext applicationContext = new ClassPathXmlApplicationContext("cn/itcast/spring/b_springhelloworld/applicationContext.xml");
// Spring的容器对象 Resource resource = new ClassPathResource("cn/itcast/spring/b_springhelloworld/applicationContext.xml"); BeanFactory beanFactory = new XmlBeanFactory(resource);
// 从中取出配置的Bean UserDao userDao = (UserDao) applicationContext.getBean("userDao"); UserService userService = (UserServiceImpl) applicationContext.getBean("userService"); }