初识Spring

 
不使用框架,我们写按照mvc结构来写代码是这样的:
业务层:service
定义一个业务层接口UserService :
1 public interface UserService {
2 public void addUser();
3 }
然后根据这个几口写一个业务层实现类UserServiceImpl :
1 public class UserServiceImpl implements UserService {
2 private UserDao userDao = new UserDaoImpl();//接口的实例化问题,自己来new 代码的耦合性很高。
3 @Override
4 public void addUser() {
5 userDao.addUser();
6 }
7  
8 }
 
持久层UserDao :
定义一个接口持久层接口:
1 public interface UserDao {
2 public void addUser();
3 }
然后根据这个接口写一个持久层实现类:
1 public class UserDaoImpl implements UserDao {
2  @Override
3 public void addUser() {
4 System.out.println("添加用户!");
5 }
6  
7 }
 
我们来模拟,层持久层获取数据返回。数据就是“添加用户”这几个字
 
然后写一个测试类来测试取回的数据:
public class UserDaoTest {
private UserService userService = new UserServiceImpl();
//private UserService userService;
@Test
public void test() {
//ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
//userService = (UserService) context.getBean("userService");
 userService.addUser();
}
}
 
整个流程下来,我们可以发现,传统的方式我们需要在业务层new 持久层的对象。在测试类中,需要new 业务层的对象。也就是说,传统的方式我们获取被依赖对象的方式是使用new关键字,自己在需要的地方自己new。
 
这种方式使用javaSE里面说的“组合”,通过组合来复用了被依赖对象里面的方法。但是这种方法是很笨重的,笨重表现如下:
1、需要用到1个依赖对象,我们就需要在这个类中主动new1个依赖类的对象,需要用到N个不同的依赖对象,我们就需要主动new N个对象。这样的话,就会使代码变得臃肿,如果业务逻辑很复杂,那这种方式也会增加代码的复杂程度,无论是开发过程中还是给别人看,都不能很容易就梳理出这里的业务。
2、耦合性很强。这一点我以前也一直不明白为什么说耦合性很强,不灵活。现在我终于明白了一点。耦合性强,就意味着可扩展性差。我们可以试想一种情况:
就是项目完成后。发布上线一年了。突然业务的木偶一个小部分需要发生了变化C对象,之前是调用A对象的,现在要改成B对象才合适。这种情况之下:
如果使用传统的new的方法,如何做出修改呢?这时候就需要将C类里面new A对像的地方改为new B对象。然后重新打包,替换原来的包。这项工作就比较负责。也根本谈不上扩展性。
 
这个时候,如果我们使用另外一种方式---工厂模式,会怎么样呢?肯定会简单很多。
比如:先创建一个工厂类,专门用于创建对象:
 1 public class MyFactoryUtils {
 2 3 public static <T> T getInstance(Class<T> c) {
 4 try {
 5 String interfaceName = c.getSimpleName();
 6 String interfaceImplName = ResourceBundle.getBundle("myinterface").getString(interfaceName);
 7 return (T) Class.forName(interfaceImplName).newInstance();
 8 } catch (Exception e) {
 9 e.printStackTrace();
10 throw new RuntimeException("加载接口实例失败.." + e);
11 }
12 }
13 }
然后业务层的代码就可以变成这样了:
1 public class UserServiceImpl implements UserService {
2 private UserDao userDao = MyFactoryUtils.getInstance(UserDao .class);
3 @Override
4 public void addUser() {
5 userDao.addUser();
6 }
7 }
 
我们通过工厂类,来获取持久层的对象,如:第2行
这个对象是怎么获取的呢?是通过配置文件获取的:
myinterface.properties.
在这个配置文件中,以key--value的方式配置好
UserDao=com.mytest.dao.imple.UserDaoImpl
 通过这种方式,我们就大大增强了之前代码的灵活性。如果我们在要更换成别的对象了。我们只要将配置文件中的value换掉就好了。
可是这种方式只能解决耦合性强的问题啊,代码臃肿的问题还是不能解决的。这个时候,spring框架的价值就体现出来了。spring框架不仅较大程度低解决了耦合性强的问题,也解决了代码臃肿的问题。那spring是如何解决耦合性强的问题的呢?
也是通过配置文件。此时spring就相当于一个bean工厂(bean可以简单地理解为那些一类对象)。跟上面的工厂模式一样,只要在配置文件中设置好相应的配置,就可以像上面一样灵活了。
此时使用spring框架的代码是这样的:
 1 public class UserDaoTest {
 2 private UserService userService;
 3 @Test
 4 public void test() {
 5 ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
 6 userService = (UserService) context.getBean("userService");
 7 userService.addUser();
 8 }
 9 }
 
我们通过spring提供的工厂对象 context 并根据这个工厂的getBean方法获取需要的对象。配置文件如下:
1 <?xml version="1.0" encoding="UTF-8"?>
2 <beans xmlns="http://www.springframework.org/schema/beans"
3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4 xsi:schemaLocation="
5 http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
6 <bean id="userService" class="com.myTest.service.impl.UserServiceImpl"></bean>
7 </beans>
这样也就可以实现像工厂模式那样降低了代码的耦合性。可是我们这里明明看到的是,代码要比工厂的那个写得更多啊?其实不是,在这个类里面:ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
这个语句,只要写一次就够了,就可以根据:
userService = (UserService) context.getBean("userService");这种方式获取不同的对象了。要获取不同的对象只要将getBean(id)。里的id换掉就可以了。这样相对工厂方式来说,代码量减少了,灵活性得到了保持。但是这种方式,还是会需要需要很多getBean方法来获取不同的对象,业务一复杂了,也会需要很多个相似的代码来获得不同的对象,灵活性上和代码冗余量可不可以更好一点呢?这个时候就是DI---依赖注入闪亮登场了。
 1 public class UserServiceImpl implements UserService {
 2 private UserDao userDao;
 3 public void setUserDao(UserDao userDao) {
 4 this.userDao = userDao;
 5 }
 6 @Override
 7 public void addUser() {
 8 userDao.addUser();
 9 }
10 }
可以看到,我们现在的代码及编程了现在这个样子,代码量明显比之前要少了很多了。配置文件如下:
 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <beans xmlns="http://www.springframework.org/schema/beans"
 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 4 xsi:schemaLocation="
 5 http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
 6 <bean id="userDao" class="cn.itcast.dao.impl.UserDaoImpl"></bean>
 7  <bean id="userService" class="cn.itcast.service.impl.UserServiceImpl">
 8  <property name="userDao" ref="userDao"></property>
 9  </bean>
10 </beans>
 
我们可以看到在业务层实现类的配置里面,配置了一个属性<property >,这个正是我们的持久层的对象。这样只要这个业务层一旦初始化,就会同时初始化持久层对象了。
这样,就代码来说,因为使用了工厂,使用了配置文件,相比什么都不使用,直接new的传统写法来说,这样的代码就更加精简,更加具有灵活性了。业务层代码,因为主要代码都集中在了业务上,所以业务也及更好理解了。也因为使用配置文件的方式,想换什么实现方式,在配置文件中将配置文件改了,也就好了,更加地灵活。
 
到这里,我们可以发现,spring核心的IOC控制反转,其实就是将创建对象的过程由原来的自己使用new关键字,转变成由spring的工厂去创建。将获得依赖对象的方式由原来的创建式,变成了配置式(在spring的配置文件中配置。)。但是,我个人无法理解的是为什么说是“反转”。我理解的反转是,本来是我为你做的事情,现在变成了你为我做,这叫反转。可是在spring这里,明显就是,本来我为自己做的事情,现在由别人为我做了。所以翻译成了”反转“,会不会对理解上有些误解呢?见仁见智了。
DI,依赖注入。关于这一点,我也是不太能理解(不能理解依赖注入成了spring的特性这一点)。这个依赖注入按照我的理解,就是同样的功能随着IOC而采用了不同的实现方式而已。因为注入,本质是将A对象注入到B对象里面,好让B对象可以使用A对象的方法,而达到复用代码的目的。这个目的使用组合的方式本身就可以实现。或者说为了达到“注入”这种更形象的样子(不考虑spring本身,单论注入),使用组合,完全可以实现。所以为什么依赖注入成了Spring专有的了呢?所以我认为,所谓依赖注入:实际上也就是代码复用的另一种形式,或者干脆说是组合的另外一种形式罢了。(这个问题我的确是有些钻牛角尖了,欢迎多多批评。)
posted @ 2018-07-08 22:45 lukely 阅读(...) 评论(...) 编辑 收藏