Spring最主要的特点有两个:IOC和AOP,这也是为了解决经常碰到的问题:
1)对象太多如何管理
2)共同逻辑和业务逻辑纠缠在一起,错综复杂,如何解耦。
IOC
IOC的全称是Inversion of Control,中文称为控制反转, Martin Flower由根据它创造了一个新词:Dependency Injection,中文称为依赖注入。这两个词讲的是一回事儿。
IOC的实质是如何管理对象,传统意义上我们使用new方式来创建对象,但在企业应用开发的过程中,大量的对象创建都在程序中维护很容易造成资源浪费,并且不利于程序的扩展。
实现IoC通常有三种方式:
1)利用接口或者继承,一般以接口较多。这种实现方式和我们平时提到的lazy load有异曲同工之妙。
2)构造函数注入。
3)属性注入(例如 setter 方法)。
eg:我们先定义两个对象
public class Person {
private String name;
private Phone phone; //person依赖于phone
public Person(){
System.out.println("我是Person()");
}
public Phone getPhone() {
return phone;
}
public void setPhone(Phone phone) {
this.phone = phone;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void call(){
phone.say();
}
}
public class Phone {
public Phone(){
System.out.println("我是Phone()");
}
public void say() {
System.out.println("你好...");
}
}
接着在spring.xml中声明bean:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="person" class="com.yc.spring.Person">
<property name="name" value="小明"/>
<property name="phone" ref="phone"/>
</bean>
<bean id="phone" class="com.yc.spring.Phone">
</bean>
</beans>
测试文件如下:
public class PersonTest extends TestCase {
@Test
public void testPerson() {
//创建spring容器对象
ApplicationContext sc=new ClassPathXmlApplicationContext("spring.xml");
Person person=(Person) sc.getBean("person");
System.out.println(person.getName());
person.call();
}
}
测试结果如下:
我是Person()
我是Phone()
小明
你好...
对象的生命周期
我们可以通过设置bean节点的scope属性来控制对象的声明周期,它包含两个可选值:
1)singleton,表明系统中对于同一个对象,只保留一个实例。
2)prototype,表明系统中每次获取bean时,都新建一个对象。
<bean id="userDaoImpl" class = "xxx.xxx.UserDaoImpl" scope="singleton"/>
<bean id="userDaoImpl2" class = "xxx.xxx.UserDaoImpl" scope="prototype"/>
我们也可以通过使用Annotation来定位Bean,这里就不详细介绍了
在使用Annotation定位Bean的基础上,我们就可以自动加载对象定义:
<context:component-scan base-package="com.yc.spring"/>
AOP
面向方面的编程,即 AOP,是一种编程技术,它允许程序员对横切关注点或横切典型的职责分界线的行为(例如日志和事务管理)进行模块化。AOP 的核心构造是方面,它将那些影响多个类的行为封装到可重用的模块中。
AOP 和 IOC 是补充性的技术,它们都运用模块化方式解决企业应用程序开发中的复杂问题。在典型的面向对象开发方式中,可能要将日志记录语句放在所有方法和 Java 类中才能实现日志功能。在 AOP 方式中,可以反过来将日志服务模块化,并以声明的方式将它们应用到需要日志的组件上。当然,优势就是 Java 类不需要知道日志服务的存在,也不需要考虑相关的代码。所以,用 Spring AOP 编写的应用程序代码是松散耦合的。
eg:以下实现一个用于计算的类
public interface ICalculatorLogging {
public int add(int i, int j);
public int divid(int i, int j);
}
@Component("iCalculatorLogging")
public class CalculatorLoggingImpl implements ICalculatorLogging {
@Override
public int add(int num1, int num2) {
int result=num1+num2;
return result;
}
@Override
public int divid(int num1, int num2) {
int result=num1-num2;
return result;
}
}
实现一个日志类:
public class LoggingAspect {
public void before(String Methodname,Object...args){
LogManager.getLogger().debug(String.format("开始执行%s方法,传入参数为:%s",Methodname,Arrays.toString(args)));
}
public void afterReturning(String Methodname,Object...args){
LogManager.getLogger().debug(String.format("执行%s方法成功,运算结果:%s",Methodname,Arrays.toString(args)));
}
}
接着实现一个日志代理类:
public class CommonProxy<T> {
private T target;
public T getProxyInstance(T t){
target = t;
ClassLoader loader=Thread.currentThread().getContextClassLoader();
Class<?>[] interfaces=target.getClass().getInterfaces();
InvocationHandler h=new InvocationHandler(){
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String methodName=method.getName();//取到代理方法的方法名
LoggingAspect loggingAspect=new LoggingAspect();
loggingAspect.before(methodName, args);
Object result=method.invoke(target, args);
loggingAspect.afterReturning(methodName, result);
return result;
}
};
return (T) Proxy.newProxyInstance(loader, interfaces, h);
}
}
测试:
public class CalculatorLoggingImplTest {
@Test
public void testadd01() {
ICalculatorLogging iCalculator=new CalculatorLoggingImpl();//执行目录
iCalculator=new CommonProxy<ICalculatorLogging>().getProxyInstance(iCalculator);//在执行过程中添加日志的代理
int result=iCalculator.add(1,2);
}
}
输出结果如下:
2018-08-28 20:47:33,699 DEBUG LoggingAspect (LoggingAspect.java:10) - 开始执行add方法,传入参数为:[1, 2]
2018-08-28 20:47:33,716 DEBUG LoggingAspect (LoggingAspect.java:14) - 执行add方法成功,运算结果:[3]