spring的核心是IOC(控制反转)和AOP(面向切面)
今天先来学习下IOC。IOC另一种说法是依赖注入(DI),它能够通过三种注入方式来将工程中耦合性很高的类进行不完全解耦。
三种注入方式:⑴构造器注入 ⑵接口注入 ⑶setter方法注入!
一般的话都会使用setter方法注入的方式来进行编程;下面来讲解一下spring依赖注入的原理(我的理解)
在java中类与类之间的关系是相互调用的关系,所以如果一个类需要调用另一个类的方法就需要创建出另一个类的对象,通过对象来调用另一个类的方法,比如这样:
一个狗的类,拥有叫和跳两种方法:
package com.ioc.test;
import java.util.jar.Attributes.Name;
public class Dog {
private String name;
public void bark(String name ) {
System.out.println(name+"叫了一下!");
}
public void jump(String name) {
System.out.println(name+"跳了一下!");
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getSize() {
return size;
}
public void setSize(int size) {
this.size = size;
}
}
而人会命令狗做一些事情,比如让狗叫或者让狗跳之类的,
定义一个person类,拥有让狗叫,让狗跳两种方法。在两种方法中传入Dog的实例对象。
package com.ioc.test;
public class Person {
private String name;
//向方法中传入Dog对象
public void holdDogBark(com.ioc.test.Dog dog){
//调用dog对象的bark方法 并使用传入的dog对象的getName方法获得dog的name属性
//传入bark方法内
dog.bark(dog.getName());
}
public void holdDogJump(Dog dog){
dog.jump(dog.getName());
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
写一个拥有main函数的测试类:
在该类中创建两个类的对象并调用Person对象两个方法 ,在方法中传入Dog对象,这样就完成了一次 Person类与Dog类之间的操作。
package com.ioc.test;
public class PersonManager {
public static void main ( String args[]){
Dog dog =new Dog();
dog.setName("小白");
Dog dog2=new Dog();
dog2.setName("小黑");
Person person=new Person();
person.holdDogBark(dog);
person.holdDogJump(dog);
person.holdDogBark(dog2);
person.holdDogJump(dog2);
}
}
但是如果这样的话会让代码拥有过高的耦合度,非常的不利于后期的维护与开发。 比如有一天Dog类中会 多一个方法run其中又调用了jump方法
而Person类中新增了一个letDogRun方法并且其中用到了holdDogJump方法 ,就需要将两个类的代码同时 改变。
其实只有这两个类的话还是看不太出来代码耦合度高会带来不便之处,但是如果调用Dog类的类很多的话 而且还有很多使用Person类方法的类,这样你在每个使用过jump和holdDogJump方法的类中都需要进行修 改。这会花费你大量的精力与时间。
而在spring中完成上面的操作会将两个类的关系从相互调用变成依赖注入。而且注入过程中完全不需要你去建立实体类。下面就是步骤
⒈首先导入spring的jar包到所建立的工程中
我这里导入了spring框架的所有jar包如图:
值得注意的是被红线圈住的包 ,我刚开始导入的时候并没有将它导入,之后在测试的时候报了一个错
NoClassDefFoundError,下面是错误信息:
Exception in thread "main" java.lang.NoClassDefFoundError:
org/apache/commons/logging/LogFactory
at org.springframework.context.support.AbstractApplicationContext.<init>
(AbstractApplicationContext.java:158)
at org.springframework.context.support.AbstractApplicationContext.<init>
(AbstractApplicationContext.java:222)
at org.springframework.context.support.AbstractRefreshableApplicationContext.<init>
(AbstractRefreshableApplicationContext.java:88)
at org.springframework.context.support.AbstractRefreshableConfigApplicationContext.
<init>(AbstractRefreshableConfigApplicationContext.java:58)
at org.springframework.context.support.AbstractXmlApplicationContext.<init>
(AbstractXmlApplicationContext.java:61)
at org.springframework.context.support.ClassPathXmlApplicationContext.<init>
(ClassPathXmlApplicationContext.java:136)
at org.springframework.context.support.ClassPathXmlApplicationContext.<init>
(ClassPathXmlApplicationContext.java:83)
at com.ioc.userioc.test.PersongUseDogTest.main(PersongUseDogTest.java:13)
Caused by: java.lang.ClassNotFoundException: org.apache.commons.logging.LogFactory
at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:331)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
... 8 more
我从网上搜了一下通过
他的博客知道了缺少该包,导入该包后异常果然消失,非常感谢这个博主!
言归正传,
⒉创建两个接口interface Person和interface Dog,在里面定义需要的方法:
Person.java
package com.ioc.userioc.interfac;
import javax.xml.ws.Holder;
//接口person
public interface Person {
//定义了两个无返回类型的方法
void holdDogBark();
void holdDogJump();
}
Dog.java
package com.ioc.userioc.interfac;
//定义一个Dog接口
public interface Dog {
//定义两个方法bark 和jump 返回类型均为string
String bark();
String jump();
}
⒊创建接口的实现类
ShortDog.java
package com.ioc.userioc.impl;
import com.ioc.userioc.interfac.Dog;
//Dog接口的实现类
public class ShortDog implements Dog{
//实现两个Dog中定义的方法
@Override
public String bark() {
System.out.println("小狗叫了一下!");
return "小狗叫了一下!";
}
@Override
public String jump() {
System.out.println("小狗跳了一下!");
return "小狗跳了一下!";
}
}
Chinese.java
package com.ioc.userioc.impl;
import com.ioc.userioc.interfac.Dog;
import com.ioc.userioc.interfac.Person;
//person的实现类
public class Chinese implements Person {
//创建的是一个接口的实例吗? 这并不是一个实例
//实例会在你调用本类中需要这个变量的方法时由spring根据applicationContext.xml文件创
//建出来
//注入的Dog类型的变量 通过setter方法注入
private com.ioc.userioc.interfac.Dog dog;
//覆盖接口person的方法
@Override
public void holdDogBark() {
System.out.println("一个人让"+dog.bark());
}
@Override
public void holdDogJump() {//
System.out.println("一个人让"+dog.jump());
}
//通过setter方法注入dog的实例对象
public void setDog(Dog dog) {
this.dog = dog;
}
}
ShortDogProxy.java
package com.ioc.userioc.impl;
import com.ioc.userioc.interfac.Dog;
// 代理类 在本类中重写dog接口的方法中可以添加一些日志或者是自己想要进行的一些操作
//这就是面向切面的一个用处
public class ShortDogProxy implements Dog{
//注入的Dog类型的变量 通过setter方法注入
private Dog dog;
@Override
public String bark() {
//方法执行前的说明语句
System.out.println("哈哈,这是dog.bark执行前");
try{
//这里的bark方法实际上就是applicationContext.xml文件中本类在里面定义
//的引用类的bark方法 引用类也必须实现Dog接口
//因为在本类中也被注入了一个Dog 变量
return dog.bark();
}finally{
//bark方法执行后
System.out.println("哈哈,这是dog.bark执行后");
}
}
@Override
public String jump() {
System.out.println("哈哈,这是dog.jump执行前");
try{
return dog.jump();
}finally{
System.out.println("哈哈,这是dog.jump执行后");
}
}
public void setDog(Dog dog) {
this.dog = dog;
}
}
⒋配置applicationContext.xml文件
applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?> <!-- spring配置文件的限制元素 ,规定了本文件会用到的标签以及格式 --> <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.springframework.org/schema/beans" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"> <!-- bean中的id是对于相应的class的标识 在 --> <bean id="chinese" class="com.ioc.userioc.impl.Chinese"> <!-- property标签就是属性标签 他的值就是注入到Chinese类中的实现Dog接口的类 在本文件中 以bean的方式定义。 name属性为dog说明这个chinese类需要一个dog类型的类的对象,就是dog接口实现类的 对象只要实现了dog接口的类都可以 --> <property name="dog" ref="shortdogV2"></property> </bean> <!-- 这个bean是一个代理类 他虽然实现了dog接口 但是再它重写的Dog接口方法中添加了 除核心操作外的一些操作--> <!-- 面向切面编程 --> <bean id="shortdogV2" class="com.ioc.userioc.impl.ShortDogProxy"> <property name="dog" ref="shortdog" /> </bean> <!--被上面的shortdogV2所引用 --> <bean id="shortdog" class="com.ioc.userioc.impl.ShortDog"></bean> </beans>
⒌建立测试类进行测试
PersongUseDogTest.java
package com.ioc.userioc.test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.ioc.userioc.interfac.Person;
// 测试类 测试
public class PersongUseDogTest {
public static void main(String[] args) {
//new 出来一个根据classpath的xml文件生成的applicationContext对象
ApplicationContext applicationContext=new ClassPathXmlApplicationContext
("applicationContext.xml");
//Person是一个接口,而person是一个引用变量,person可以指向任何实现了Person接口的类,
//而ApplicationContext的getBean方法 ,是根据xml文件中的配置和方法的参数得到一个bean
//对象
//方法中的chinese应该是bean的ID而Person.class
Person person=applicationContext.getBean("chinese", Person.class);
//调用person的方法holdDogBark 其实就是通过applicationContext.xml得到的一个实现
//person接口的类就是Chinese的对象
//然后再调用这个对象的holdDogBark方法 ,在这个方法被运行时方法需要一个实现
//Dog接口的类,这个方法会根据
//applicationContext.xml文件来
person.holdDogBark();
person.holdDogJump();
}
}
结果
二月 24, 2017 12:35:06 下午 org.springframework.context.support.
AbstractApplicationContext prepareRefresh
信息: Refreshing org.springframework.context.support.
ClassPathXmlApplicationContext@6c589e: startup date [Fri Feb 24 12:35:06 CST 2017];
root of context hierarchy
二月 24, 2017 12:35:06 下午 org.springframework.beans.factory.xml.
XmlBeanDefinitionReader
loadBeanDefinitions
信息: Loading XML bean definitions from class path resource [applicationContext.xml]
哈哈,这是dog.bark执行前
小狗叫了一下!
哈哈,这是dog.bark执行后
一个人让小狗叫了一下!
哈哈,这是dog.jump执行前
小狗跳了一下!
哈哈,这是dog.jump执行后
一个人让小狗跳了一下!