Spring IoC
Spring IoC就是控制反转,也被称为依赖注入(Dependency Injection, DI),是面向对象编程中的一种设计理念,用来降低程序代码之间的耦合度。
依赖是什么:
依赖就是在代码中通过局部变量、方法参数、返回值等建立的对于其他对象的调用关系。
1 /** 2 * @content 接口 3 * @author Gawain 4 * @date 2017-8-15下午8:02:37 5 */ 6 public interface DependDemo { 7 /** 8 * 显示信息 9 */ 10 void showInfo(); 11 } 12 13 /** 14 * @content 实现类 15 * @author Gawain 16 * @date 2017-8-15下午8:02:30 17 */ 18 public class DependDemoImpl implements DependDemo { 19 /** 20 * 实现方法 21 */ 22 @Override 23 public void showInfo() { 24 System.out.println("你好"); 25 } 26 } 27 28 /** 29 * @content 测试类 30 * @author Gawain 31 * @date 2017-8-15下午8:02:09 32 */ 33 public class Demo { 34 public static void main(String[] args) { 35 //实例化依赖的对象,此时,Demo类依赖于DependDemoImpl类 36 DependDemo demo = new DependDemoImpl(); 37 //调用方法 38 demo.showInfo(); 39 } 40 }
通过上面的代码可以看出,Demo类和DependDemoImpl类高度耦合,如果需求变化需要替换DependDemo接口的实现类DependDemoImpl的话,那么Demo中的代码也需要进行改动。
解决方法(控制反转):
创建一个对象工厂,将创建实例的工作交给工厂去做,获得对象时不通过new的方式而是通过工厂来获得对象。
1 /** 2 * @content 对象工厂 3 * @author Gawain 4 * @date 2017-8-15下午8:11:27 5 */ 6 public class Factory { 7 /** 8 * 返回对象实例 9 * @return 10 */ 11 public static DependDemo getDepend() { 12 return new DependDemoImpl(); 13 } 14 } 15 16 /** 17 * @content 测试类 18 * @author Gawain 19 * @date 2017-8-15下午8:02:09 20 */ 21 public class Demo { 22 public static void main(String[] args) { 23 //通过对象工厂获得实例 24 DependDemo demo = Factory.getDepend(); 25 //调用方法 26 demo.showInfo(); 27 } 28 }
通过上面的代码可以看出,Demo类不再依靠自身的代码去获得所依赖的具体的DependDemo对象,而是将这一工作交给了对象工厂去做,如果DependDemo接口的实现类需要替换的话,只要在工厂类修改代码即可。此时由工厂来控制创建对象而不是Demo本身,这就是控制反转。
按照上面的方法虽然可以解决问题,但是大量的工厂类会被引入到开发过程中,大大的增加了开发的工作量。此时我们就需要用到Spring了。
Spring为我们提供了完整的IoC实现,让我们得以专注于业务类和DAO类的设计。
开发Spring项目
1.下载Spring的jar包并添加到项目中。也可以使用MyEclipse来简化这一步骤。右击项目,选择MyEclipse-->Add Spring Capabilities...然后直接点击Finish。
2.编写Spring配置文件
1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans 3 xmlns="http://www.springframework.org/schema/beans" 4 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 5 xmlns:p="http://www.springframework.org/schema/p" 6 xsi:schemaLocation="http://www.springframework.org/schema/beans 7 http://www.springframework.org/schema/beans/spring-beans-3.1.xsd"> 8 <!-- 通过bean元素声明需要Spring创建的实例。该实例的类型通过class属性指定,并通过id属性为该实例指定一个名称,以便于访问 --> 9 <bean id="dependDemo" class="com.jazz.demo.DependDemoImpl"/> 10 </beans>
补充:
1.applicationContext.xml是MyEclipse自动帮你添加的。如果你是自己导的jar包的话,这一文件需自己手动创建。
2.上述代码中的id属性也可以使用name属性来完成相同的工作。二者的不同之处在于id属性只能指定一个名称,而name属性可以指定多个名称,多个名称之间使用空格或者逗号隔开。
3.class属性是类的全限定类名。
3.编写代码通过Spring获取DependDemo实例
1 import org.springframework.context.ApplicationContext; 2 import org.springframework.context.support.ClassPathXmlApplicationContext; 3 4 /** 5 * @content 测试类 6 * @author Gawain 7 * @date 2017-8-15下午8:02:09 8 */ 9 public class Demo { 10 public static void main(String[] args) { 11 //通过ClassPathXmlApplicationContext实例化Spring的上下文 12 ApplicationContext con = new ClassPathXmlApplicationContext("applicationContext.xml"); 13 //通过ApplicationContext的getBean()方法根据id来获取bean的实例 14 DependDemo demo = con.getBean("dependDemo", DependDemoImpl.class); 15 //调用方法 16 demo.showInfo(); 17 } 18 }
补充:
1.在上面的代码中,ApplicationContext是一个接口,负责读取Spring配置文件,管理对象的加载、生成,维护Bean对象与Bean对象之间的依赖关系,负责Bean的生命周期等。
2.ClassPathXmlApplicationContext是ApplicationContext接口的实现类,用于从classpath路径中读取Spring配置文件。classpath路径就是src文件夹。
3.写在配置文件中的Bean会在ClassPathXmlApplicationContext加载Spring配置文件时创建生成。
小结:
Spring IoC它就是一个容器,负责管理Bean的创建以及管理Bean与Bean之间的关系等等。
依赖注入
依赖注入是什么?
依赖注入就是将Bean的创建以及为属性赋值的工作交给Spring容器来做,从而避免组件之间以硬编码的方式耦合在一起。
上文提到过,Spring IoC不仅可以管理对象的加载与生成,还可以管理Bean与Bean之间的依赖关系。除此之外,还可以在创建Bean时为Bean中的属性赋初值。
下面再写一个小例子演示一下依赖注入。
1 /** 2 * @content 书实体类 3 * @author Gawain 4 * @date 2017-8-15下午9:16:43 5 */ 6 public class Book { 7 //书籍名称 8 private String bookName; 9 //重写toString方法 10 @Override 11 public String toString() { 12 return "Book [bookName=" + bookName + "]"; 13 } 14 15 public String getBookName() { 16 return bookName; 17 } 18 19 public void setBookName(String bookName) { 20 this.bookName = bookName; 21 } 22 } 23 24 25 /** 26 * @content 用户实体类 27 * @author Gawain 28 * @date 2017-8-15下午9:15:52 29 */ 30 public class User { 31 //姓名 32 private String name; 33 //年龄 34 private int age; 35 //正在读的书籍 36 private Book book; 37 //重写toString方法 38 @Override 39 public String toString() { 40 return "User [name=" + name + ", age=" + age + ", book=" + book + "]"; 41 } 42 public String getName() { 43 return name; 44 } 45 public void setName(String name) { 46 this.name = name; 47 } 48 public int getAge() { 49 return age; 50 } 51 public void setAge(int age) { 52 this.age = age; 53 } 54 public Book getBook() { 55 return book; 56 } 57 public void setBook(Book book) { 58 this.book = book; 59 } 60 }
1 /** 2 * @content 业务逻辑层接口 3 * @author Gawain 4 * @date 2017-8-15下午9:13:48 5 */ 6 public interface UserService { 7 /** 8 * 显示信息 9 */ 10 void showInfo(); 11 } 12 13 14 /** 15 * @content 业务逻辑层实现类 16 * @author Gawain 17 * @date 2017-8-15下午9:39:25 18 */ 19 public class UserServiceImpl implements UserService { 20 private User user; 21 public User getUser() { 22 return user; 23 } 24 public void setUser(User user) { 25 this.user = user; 26 } 27 //显示用户信息 28 @Override 29 public void showInfo() { 30 System.out.println(user); 31 } 32 }
1 /** 2 * @content user控制层,此处作为测试类 3 * @author Gawain 4 * @date 2017-8-15下午9:31:35 5 */ 6 public class UserController { 7 public static void main(String[] args) { 8 ApplicationContext con = new ClassPathXmlApplicationContext("applicationContext.xml"); 9 UserService userService = con.getBean("userService", UserServiceImpl.class); 10 userService.showInfo(); 11 } 12 }
1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans 3 xmlns="http://www.springframework.org/schema/beans" 4 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 5 xmlns:p="http://www.springframework.org/schema/p" 6 xsi:schemaLocation="http://www.springframework.org/schema/beans 7 http://www.springframework.org/schema/beans/spring-beans-3.1.xsd"> 8 <!-- 通过bean元素声明需要Spring创建的实例。该实例的类型通过class属性指定,并通过id属性为该实例指定一个名称,以便于访问 --> 9 <bean id="dependDemo" class="com.jazz.demo.DependDemoImpl"/> 10 <!-- 声明book对象 --> 11 <bean id="book" class="com.jazz.pojo.Book"> 12 <!-- 为属性赋值 --> 13 <property name="bookName" value="Java数据结构和算法" /> 14 </bean> 15 <!-- 声明user对象 --> 16 <bean id="user" class="com.jazz.pojo.User"> 17 <property name="name" value="Gawain" /> 18 <property name="age" value="18" /> 19 <!-- 使用ref引用book对象,添加依赖关系 --> 20 <property name="book" ref="book" /> 21 </bean> 22 <!-- 声明service对象 --> 23 <bean id="userService" class="com.jazz.services.impl.UserServiceImpl"> 24 <property name="user" ref="user"/> 25 </bean> 26 </beans>
补充:
1.为属性赋值的方式有很多种,除了上文的设值注入外,还有构造注入和p命名空间注入。
构造注入语法:
<constructor-arg index="" value="" />
其中value是值,index是指构造方法中的第几个参数,从0开始。
p命名空间注入语法:
1 <beans xmlns="http://www.springframework.org/schema/beans" 2 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 3 xmlns:p="http://www.springframework.org/schema/p" 4 xsi:schemaLocation="http://www.springframework.org/schema/beans 5 http://www.springframework.org/schema/beans/spring-beans.xsd"> 6 <bean name="john-classic" class="com.example.Person"> 7 <property name="name" value="John Doe"/> 8 <property name="spouse" ref="jane"/> 9 </bean> 10 <bean name="john-modern" 11 class="com.example.Person" 12 p:name="John Doe" 13 p:spouse-ref="jane"/> 14 <bean name="jane" class="com.example.Person"> 15 <property name="name" value="Jane Doe"/> 16 </bean> 17 </beans>
使用p命名空间注入需要在beans中添加两个url,基本类型的属性使用p:属性名=属性值的方式注入,引用类型的属性使用p:属性名-ref=引用bean的id的方式注入。
2.除了基本数据类型和自定义数据类型之外,Spring还支持很多数据类型,如下图所示。此处就不一一列举了。大家可以去Spring的帮助文档中查看。
小结:
依赖注入其实就是将对象之间的依赖关系交给Spring来管理和组装了。不要看它名字说的很“高大上”,其实实现起来很简单。
Spring AOP
Spring AOP简介:
Spring AOP就是面向切面编程(Aspect Oriented Programming, AOP),是软件编程思想发展到一定阶段的产物,是面向对象编程(Object Oriented Programming, OOP)的有益补充。AOP一般适用于具有横切逻辑的场合,例如访问控制、事务管理、性能检测等。
横切逻辑是什么:
大家先来看一段代码
1 public class UserController { 2 //声明日志 3 static Logger log = Logger.getLogger(UserController.class); 4 public static void main(String[] args) { 5 //在方法执行前输出日志 6 log.info("显示用户信息"); 7 //使用try-catch来进行对异常的处理 8 try { 9 ApplicationContext con = new ClassPathXmlApplicationContext("applicationContext.xml"); 10 UserService userService = con.getBean("userService", UserServiceImpl.class); 11 userService.showInfo(); 12 } catch (Exception e) { 13 log.error("显示用户信息失败", e); 14 } 15 } 16 }
上面的代码是一段典型的日志输出+异常处理的代码,从上面的代码可以看出,代码中添加了大量的日志和异常处理的代码,而我们实际的业务代码只有3行。
日志、异常处理、事务控制是一个健壮的业务系统所必须的,但是为了保证系统健壮可用,就需要在众多的业务方法中“反复”编写类似的代码,使得原本就很复杂的业务处理代码变得更加复杂。
在业务系统中,总有一些散落、渗透到系统各处且不得不处理的事情,这些穿插在既定业务中的操作就是所谓的“横切逻辑”,也被称为“切面”。
面向切面编程极大的简化了上面代码中“重复”但又不得不写的代码,可以使我们在不改变原程序的基础上为代码段增加新的功能,对代码段进行增强处理。它的设计思想来源于代理设计模式。
Spring AOP基本概念:
1.切面(Aspect):一个模块化的横切逻辑(或横切关注点),可能会横切多个对象。
2.连接点(Join Point):程序执行中的某个具体的执行点。
3.增强处理(Advice):切面在某个特定连接点上执行的代码逻辑。
4.切入点(Pointcut):对连接点的特征进行描述,可以使用正则表达式。增强处理和一个切入点表达式关联,并在与这个切入点匹配的某个连接点上运行。
5.目标对象(Target object):被一个或多个切面增强的对象。
6.AOP代理(AOP proxy):由AOP框架所创建的对象,实现执行增强处理方法等功能。
7.织入(Weaving):将增强处理连接到应用程序中的类型或对象上的过程。
8.增强处理类型:有前置增强、后置增强、环绕增强、异常抛出增强、最终增强等等。这些增强处理实现方式都差不多。
除了上文说的技术之外,spring的帮助文档也给我们提供了相当全面且详细的说明。